요청 처리 방법

리전 ID

REGION_ID는 앱을 만들 때 선택한 리전을 기준으로 Google에서 할당하는 축약된 코드입니다. 일부 리전 ID는 일반적으로 사용되는 국가 및 주/도 코드와 비슷하게 표시될 수 있지만 코드는 국가 또는 주/도와 일치하지 않습니다. 2020년 2월 이후에 생성된 앱의 경우 REGION_ID.r이 App Engine URL에 포함됩니다. 이 날짜 이전에 만든 기존 앱의 경우 URL에서 리전 ID는 선택사항입니다.

리전 ID에 대해 자세히 알아보세요.

이 문서는 App Engine 애플리케이션이 요청을 수신하고 응답을 전송하는 방법을 설명합니다.

자세한 내용은 요청 헤더 및 응답 참조를 참조하세요.

애플리케이션이 서비스를 사용한다면 요청 주소를 특정 서비스 또는 해당 서비스의 특정 버전으로 지정할 수 있습니다. 서비스 주소 지정에 대한 자세한 내용은 요청 라우팅 방법을 참조하세요.

요청 처리

애플리케이션은 웹 서버를 시작하고 요청을 처리하는 작업을 담당합니다. 사용 중인 개발 언어용으로 제공되는 모든 웹 프레임워크를 사용할 수 있습니다.

App Engine에서 애플리케이션의 여러 인스턴스를 실행하며 각 인스턴스에는 요청 처리를 위한 자체 웹 서버가 있습니다. 요청이 임의의 인스턴스로 라우팅될 수 있으므로, 동일한 사용자가 보내는 연속 요청이 같은 인스턴스로 전송되지 않을 수 있습니다. 인스턴스 한 개가 여러 요청을 동시에 처리할 수 있습니다. 인스턴스 수는 트래픽의 변화에 따라 자동으로 조정될 수 있습니다. 또한 app.yaml 파일에서 max_concurrent_requests 요소를 설정하여 인스턴스 한 개가 처리할 수 있는 동시 요청 수를 변경할 수 있습니다.

App Engine이 애플리케이션에 대한 웹 요청을 수신하면 애플리케이션의 app.yaml 구성 파일의 설명대로 URL에 해당하는 핸들러 스크립트를 호출합니다. Python 2.7 런타임은 이전 버전과 호환되도록 WSGI 표준CGI 표준을 지원합니다. WSGI를 사용하는 것이 좋으며, Python 2.7의 일부 기능은 WSGI가 없으면 작동하지 않습니다. 애플리케이션의 스크립트 핸들러 구성에 따라 요청이 WSGI를 사용하여 처리되는지 아니면 CGI를 사용하여 처리되는지 결정됩니다.

다음 Python 스크립트는 HTTP 헤더 및 Hello, World! 메시지를 사용하여 요청에 응답합니다.

import webapp2

class MainPage(webapp2.RequestHandler):
    def get(self):
        self.response.headers["Content-Type"] = "text/plain"
        self.response.write("Hello, World!")

app = webapp2.WSGIApplication(
    [
        ("/", MainPage),
    ],
    debug=True,
)

여러 요청을 각 웹 서버에 동시에 전달하려면 threadsafe: trueapp.yaml 파일에 추가하여 애플리케이션을 threadsafe로 표시합니다. CGI를 사용하는 스크립트 핸들러가 있으면 동시 요청이 불가능합니다.

할당량 및 한도

App Engine은 트래픽 증가에 따라 애플리케이션에 자동으로 리소스를 할당합니다. 그러나 여기에는 다음과 같은 제한사항이 적용됩니다.

  • App Engine은 애플리케이션이 요청에 응답하는 시간이 1초 미만인, 지연 시간이 짧은 애플리케이션을 위해 자동 확장 용량을 예약합니다.

  • CPU 사용량이 많은 애플리케이션의 경우, 동일 서버의 다른 애플리케이션과 효율적으로 리소스를 공유하기 위해 지연 시간이 추가로 발생할 수 있습니다. 정적 파일에 대한 요청은 지연 시간 한도에서 제외됩니다.

애플리케이션에 수신되는 각 요청은 요청 한도에 반영됩니다. 요청에 대한 응답으로 전송되는 데이터는 발신 대역폭(청구 가능) 한도에 반영됩니다.

HTTP 및 HTTPS(보안) 요청은 모두 요청, 수신 대역폭(청구 가능), 발신 대역폭(청구 가능) 한도에 반영됩니다. Google Cloud 콘솔의 할당량 세부정보 페이지에서도 정보 제공 목적으로 보안 요청, 보안 수신 대역폭, 보안 발신 대역폭을 별도의 값으로 보고합니다. HTTPS 요청만 이러한 값에 반영됩니다. 자세한 내용은 할당량 페이지를 참조하세요.

다음은 요청 핸들러 사용 시 적용되는 한도입니다.

한도 용량
요청 크기 32MB
응답 크기 32MB
요청 제한 시간 앱에서 사용하는 확장 유형에 따라 다름
최대 총 파일 수(앱 파일과 정적 파일) 총 10,000개
디렉터리당 1,000개
애플리케이션 파일 최대 크기 32MB
정적 파일 최대 크기 32MB
모든 애플리케이션과 정적 파일의 최대 총 크기 첫 1GB는 무료
첫 1GB 이후 1GB당 월 $0.026
대기 중인 요청 제한 시간 10초
단일 요청 헤더 필드의 최대 크기 표준 환경의 2세대 런타임의 경우 8KB입니다. 해당 런타임에 대한 요청의 헤더 필드가 8KB를 초과하면 HTTP 400 오류가 반환됩니다.

요청 한도

모든 HTTP/2 요청은 애플리케이션 서버로 전달될 때 HTTP/1.1 요청으로 변환됩니다.

응답 한도

  • 동적 응답은 32MB로 제한됩니다. 스크립트 핸들러가 이 한도를 초과하는 응답을 생성하면 서버가 500 내부 서버 오류 상태 코드와 함께 빈 응답을 반환합니다. 이 제한은 기존 Blobstore 또는 Cloud Storage의 데이터를 제공하는 응답에는 적용되지 않습니다.

  • 2세대 런타임의 응답 헤더 한도는 8KB입니다. 이 제한을 초과하는 응답 헤더는 upstream sent too big header while reading response header from upstream을 표시하는 로그와 함께 HTTP 502 오류를 반환합니다.

요청 헤더

수신되는 HTTP 요청에는 클라이언트가 전송한 HTTP 헤더가 포함되어 있습니다. 보안을 위해 일부 헤더는 애플리케이션에 도달하기 전 중간 프록시에 의해 삭제 또는 수정됩니다.

자세한 내용은 요청 헤더 참조를 확인하세요.

요청 제한 시간 처리

App Engine은 단기 요청, 일반적으로 수백 밀리초 정도 지속되는 요청을 사용하는 애플리케이션에 최적화되어 있습니다. 효율적인 앱은 대부분의 요청에 신속하게 응답합니다. 그렇지 않은 앱은 App Engine 인프라에 맞춰 제대로 확장되지 않습니다. 이러한 수준의 성능을 보장하기 위해 모든 앱에서 응답해야 하는 시스템 적용 최대 요청 제한 시간이 있습니다.

앱이 이 기한을 초과하면 App Engine은 요청 핸들러를 중단합니다. Python 런타임 환경은 google.appengine.runtime에서 DeadlineExceededError 예외를 발생시켜서 이를 수행합니다. 요청 핸들러가 이 예외를 포착하지 못하면 포착되지 않은 모든 예외와 마찬가지로 런타임 환경이 HTTP 500 서버 오류를 클라이언트에 반환합니다.

요청 핸들러는 이 오류를 포착하여 응답을 맞춤설정할 수 있습니다. 런타임 환경은 예외를 발생시킨 후에 요청 핸들러에 커스텀 응답을 준비할 시간을 조금 더 줍니다(1초 미만).

class TimerHandler(webapp2.RequestHandler):
    def get(self):
        from google.appengine.runtime import DeadlineExceededError

        try:
            time.sleep(70)
            self.response.write("Completed.")
        except DeadlineExceededError:
            self.response.clear()
            self.response.set_status(500)
            self.response.out.write("The request did not complete in time.")

두 번째 기한까지 핸들러가 응답을 반환하지 않거나 예외를 발생시키지 않으면 핸들러가 중지되고 기본 오류 응답이 반환됩니다.

응답

App Engine은 Request로 핸들러 스크립트를 호출하고 스크립트가 반환될 때까지 기다립니다. 표준 출력 스트림에 작성된 모든 데이터가 HTTP 응답으로 전송됩니다.

생성하는 응답에는 크기 제한이 적용되며 응답이 클라이언트에 반환되기 전에 이 응답을 수정할 수 있습니다.

자세한 내용은 요청 응답 참조를 확인하세요.

스트리밍 응답

App Engine은 요청이 처리되는 동안 증분 청크의 데이터가 클라이언트에 전송되는 스트리밍 응답을 지원하지 않습니다. 위 설명과 같이 코드의 모든 데이터가 수집되어 단일 HTTP 응답으로 전송됩니다.

응답 압축

App Engine은 압축된(gzipped) 콘텐츠를 지원하는 클라이언트에 이 콘텐츠를 제공하기 위해 최선을 다하고 있습니다. 콘텐츠 압축 여부를 결정하기 위해 App Engine은 요청을 수신할 때 다음을 수행합니다.

  1. 요청에서 Accept-EncodingUser-Agent 헤더를 모두 확인하여 클라이언트가 압축된 응답을 안정적으로 수신할 수 있는지 확인합니다. 이러한 방법은 일반 브라우저에서 잘 알려진 gzip 콘텐츠 관련 버그의 일부를 예방해줍니다.

  2. 응답 핸들러에 구성한 Content-Type 헤더를 확인하고 콘텐츠를 압축하는 것이 적절한지 확인합니다. 일반적으로 텍스트 기반 콘텐츠 유형은 압축되지만 바이너리 콘텐츠 유형은 압축되지 않습니다.

다음에 유의하세요.

  • 클라이언트는 Accept-EncodingUser-Agent 요청 헤더를 모두 gzip으로 설정하여 텍스트 기반 콘텐츠 유형을 강제로 압축할 수 있습니다.

  • 요청의 Accept-Encoding 헤더에 gzip이 지정되지 않으면 App Engine이 응답 데이터를 압축하지 않습니다.

  • Google 프런트엔드에서 App Engine 정적 파일 및 디렉터리 핸들러에서 수신한 응답을 캐시합니다. 어떤 유형의 응답 데이터가 먼저 캐시되는지, 응답에 어떤 Vary 헤더를 지정했는지, 요청에 어떤 헤더가 포함되는지 등의 다양한 요소에 따라 클라이언트에서 압축 데이터를 요청했지만 압축되지 않은 데이터를 수신할 수도 있고 그 반대일 수도 있습니다. 자세한 내용은 응답 캐싱을 참조하세요.

응답 캐싱

Google 프런트엔드와 잠재적으로 사용자 브라우저 및 기타 중간 캐싱 프록시 서버가 응답에 지정한 표준 캐싱 헤더 설명에 따라 앱의 응답을 캐시합니다. 프레임워크를 통해 코드에서 직접 지정하거나 App Engine 정적 파일 및 디렉터리 핸들러를 통해 이러한 응답 헤더를 지정할 수 있습니다.

Google 프런트엔드에서 캐시 키는 요청의 전체 URL입니다.

정적 콘텐츠 캐싱

업데이트된 정적 콘텐츠가 게시되는 즉시 클라이언트에서 이를 확인하려면 css/v1/styles.css와 같은 버전 디렉터리에서 정적 콘텐츠를 제공하는 것이 좋습니다. Google 프런트엔드는 캐시가 만료될 때까지 캐시를 검증하지 않고 업데이트된 콘텐츠를 확인합니다. 캐시가 만료된 후에도 요청 URL의 콘텐츠가 변경될 때까지 캐시가 업데이트되지 않습니다.

app.yaml에서 설정할 수 있는 다음 응답 헤더는 Google 프런트엔드에서 콘텐츠를 캐시하는 방식과 시기에 영향을 줍니다.

  • Google 프런트엔드에서 콘텐츠를 캐시하려면 Cache-Controlpublic으로 설정해야 합니다. Cache-Control private 또는 no-store 지시문을 지정하지 않는 한 Google 프런트엔드에서 캐시할 수도 있습니다. app.yaml에 이 헤더를 설정하지 않으면 App Engine이 정적 파일이나 디렉터리 핸들러에서 처리하는 모든 응답에 자동으로 헤더를 추가합니다. 자세한 내용은 추가 또는 대체되는 헤더를 참조하세요.

  • Vary: 요청에서 전송되는 헤더를 기반으로 URL에 대해 서로 다른 응답을 반환하도록 하려면 Vary 응답 헤더에 Accept, Accept-Encoding, Origin, X-Origin 값 중 하나 이상을 설정합니다.

    카디널리티가 발생할 가능성이 높기 때문에 다른 Vary 값에서는 데이터가 캐시되지 않습니다.

    예를 들면 다음과 같습니다.

    1. 다음 응답 헤더를 지정합니다.

      Vary: Accept-Encoding

    2. 앱에서 Accept-Encoding: gzip 헤더가 포함된 요청을 받습니다. App Engine은 압축된 응답을 반환하고 Google 프런트엔드는 응답 데이터의 gzip 버전을 캐시합니다. Accept-Encoding: gzip 헤더가 포함된 이 URL의 모든 후속 요청은 캐시가 만료된 후 콘텐츠가 변경되어 캐시가 무효화되기 전까지는 캐시에서 gzip 처리된 데이터를 수신합니다.

    3. 앱에서 Accept-Encoding 헤더를 포함하지 않는 요청을 수신합니다. App Engine은 압축되지 않은 응답을 반환하고 Google 프런트엔드는 압축되지 않은 버전의 응답 데이터를 캐시합니다. Accept-Encoding 헤더가 포함되지 않은 이 URL의 모든 후속 요청은 캐시가 무효화될 때까지 캐시에서 압축된 데이터를 수신합니다.

    Vary 응답 헤더를 지정하지 않으면 Google 프런트엔드는 URL에 대한 단일 캐시 항목을 만들고 요청의 헤더와 관계없이 모든 요청에 이 항목을 사용합니다. 예를 들면 다음과 같습니다.

    1. Vary: Accept-Encoding 응답 헤더를 지정하지 않습니다.
    2. 요청에 Accept-Encoding: gzip 헤더가 포함되고 응답 데이터의 gzip 버전이 캐시됩니다.
    3. 두 번째 요청에는 Accept-Encoding: gzip 헤더가 포함되어 있지 않습니다. 그러나 캐시에는 응답 데이터의 gzip 버전이 포함되어 있으므로 클라이언트가 압축되지 않은 데이터를 요청했더라도 응답이 gzip으로 압축됩니다.

요청에 포함된 헤더는 캐싱에도 영향을 미칩니다.

  • 요청에 Authorization 헤더가 포함된 경우 Google 프런트엔드에서 콘텐츠를 캐시하지 않습니다.

캐시 만료

기본적으로 App Engine 정적 파일 및 디렉터리 핸들러가 응답에 추가하는 캐싱 헤더는 클라이언트 및 Google 프런트엔드와 같은 웹 프록시에 10분 후에 캐시를 만료하도록 지시합니다.

지정된 만료 시간으로 파일이 전송된 후에는 사용자가 자신의 브라우저 캐시를 삭제하더라도 웹 프록시 캐시에서 파일을 지울 수 있는 일반적인 방법은 없습니다. 앱의 새 버전을 다시 배포해도 캐시는 재설정되지 않습니다. 따라서 정적 파일을 수정할 계획이 있다면 만료 시간이 짧아야 합니다(1시간 미만). 대부분의 경우 만료 시간 기본값인 10분이 적합합니다.

app.yaml 파일에서 default_expiration 요소를 지정하면 모든 정적 파일 및 디렉터리 핸들러의 기본 만료 시간을 변경할 수 있습니다. 개별 핸들러의 특정 만료 시간을 설정하려면 app.yaml 파일에서 핸들러 요소 안의 expiration 요소를 지정합니다.

만료 요소 시간에 지정된 값은 Cache-ControlExpires HTTP 응답 헤더를 설정하는 데 사용됩니다.

앱 캐싱

여러 파일이 한 모듈을 가져오더라도 독립형 Python 애플리케이션이 해당 모듈을 한 번만 로드하는 것과 유사하게 Python 런타임 환경은 가져온 모듈을 단일 웹 서버에서 여러 요청 간에 캐시 처리합니다. WSGI 핸들러는 모듈이기 때문에 요청 간에 캐시 처리됩니다. CGI 핸들러 스크립트는 main() 루틴을 제공하는 경우에만 캐시됩니다. 그렇지 않으면 CGI 핸들러 스크립트가 모든 요청에 대해 로드됩니다.

앱 캐싱은 응답 시간에 상당한 이점을 가져다 줍니다. 아래에 설명된 대로 모든 CGI 핸들러 스크립트가 main() 루틴을 사용하는 것이 좋습니다.

가져오기 항목 캐시 처리

효율성을 위해 웹 서버는 가져온 모듈을 메모리에 유지하며, 같은 서버의 같은 애플리케이션에 대한 후속 요청이 있을 경우 해당 모듈을 다시 로드하거나 다시 평가하지 않습니다. 대부분의 모듈은 가져올 때 전역 데이터를 초기화하거나 다른 부작용을 일으키지 않기 때문에 모듈을 캐싱한다고 해서 애플리케이션의 동작이 바뀌지 않습니다.

모든 요청에 대해 평가되는 모듈에 종속된 모듈을 가져오는 경우 애플리케이션은 이 캐싱 동작을 수용해야 합니다.

CGI 핸들러 캐싱

가져온 모듈 외에 CGI 핸들러 스크립트 자체도 캐시 처리하도록 App Engine에 지시할 수 있습니다. 핸들러 스크립트가 main()이라는 함수를 정의하는 경우 스크립트와 해당 전역 환경이 가져온 모듈처럼 캐시 처리됩니다. 지정된 웹 서버에서 스크립트에 대한 첫 번째 요청은 스크립트를 정상적으로 평가합니다. 후속 요청의 경우에는 App Engine이 캐시 처리된 환경에서 main() 함수를 호출합니다.

핸들러 스크립트를 캐시하려면 App Engine이 인수 없이 main()을 호출할 수 있어야 합니다. 핸들러 스크립트가 main() 함수를 정의하지 않거나 main() 함수에 기본값이 없는 인수가 필요한 경우 App Engine은 모든 요청에 대해 전체 스크립트를 로드하고 평가합니다.

파싱된 Python 코드를 메모리에 유지하면 시간을 절약하고 더 빠르게 응답할 수 있습니다. 전역 환경을 캐싱하면 다음과 같은 기타 상황에서도 유용할 수 있습니다.

  • 컴파일된 정규 표현식. 모든 정규 표현식은 파싱되어 컴파일된 형태로 저장됩니다. 컴파일된 정규 표현식을 전역 변수에 저장한 후 앱 캐싱을 사용하여 컴파일된 객체를 요청 사이에서 재사용할 수 있습니다.

  • GqlQuery 객체. GQL 쿼리 문자열은 GqlQuery 객체가 생성될 때 파싱됩니다. GqlQuery 객체를 매개변수 결합 및 bind() 메서드와 함께 재사용하는 것이 매번 객체를 다시 생성하는 것보다 더 빠릅니다. GqlQuery 객체를 매개변수 결합과 함께 전역 변수의 값에 저장한 후 각 요청의 새 매개변수 값을 결합하여 재사용할 수 있습니다.

  • 구성 및 데이터 파일. 애플리케이션이 파일에서 구성 데이터를 로드 및 파싱하는 경우 애플리케이션은 파싱된 데이터를 메모리에 보존하여 요청마다 파일을 다시 로드하는 것을 방지할 수 있습니다.

핸들러 스크립트는 가져올 때 main()을 호출해야 합니다. App Engine은 이 스크립트를 가져올 때 main()이 호출되리라 예상하기 때문에 서버에서 요청 핸들러를 처음으로 로드할 때 App Engine이 이 함수를 호출하지 않습니다.

main()을 통한 앱 캐싱은 CGI 핸들러의 응답 시간을 상당히 개선시키므로 CGI를 사용하는 모든 애플리케이션에 사용하는 것이 좋습니다.

로깅

App Engine 웹 서버는 웹 요청에 응답하기 위해 핸들러 스크립트가 표준 출력 스트림에 쓰는 모든 항목을 캡처합니다. 또한 핸들러 스크립트가 표준 오류 스트림에 쓰는 모든 항목도 캡처하여 로그 데이터로 저장합니다. 각 요청에는 요청 시작 시간을 기반으로 전역 고유 식별자인 request_id가 할당됩니다. Cloud Logging을 사용하여 Google Cloud 콘솔에서 애플리케이션의 로그 데이터를 볼 수 있습니다.

로그 수준('디버그', '정보', '경고', '오류', '심각') 등의 로깅 개념을 이해하기 위해 App Engine Python 런타임 환경은 Python 표준 라이브러리의 로깅 모듈을 특별히 지원합니다.

import logging

import webapp2

class MainPage(webapp2.RequestHandler):
    def get(self):
        logging.debug("This is a debug message")
        logging.info("This is an info message")
        logging.warning("This is a warning message")
        logging.error("This is an error message")
        logging.critical("This is a critical message")

        try:
            raise ValueError("This is a sample value error.")
        except ValueError:
            logging.exception("A example exception log.")

        self.response.out.write("Logging example.")

app = webapp2.WSGIApplication([("/", MainPage)], debug=True)

환경

실행 환경은 여러 환경 변수를 자동으로 설정합니다. app.yaml에서 더 많이 설정할 수 있습니다. 자동으로 설정되는 변수 중에서 몇 가지는 App Engine에 중요하며 다른 것들은 WSGI 또는 CGI 표준의 일부입니다. Python 코드는 os.environ 사전을 사용하여 이러한 변수에 액세스할 수 있습니다.

다음 환경 변수는 App Engine에 고유합니다.

  • CURRENT_VERSION_ID: 현재 실행 중인 애플리케이션의 주 버전 및 부 버전입니다(예: 'X.Y'). 주 버전 번호('X')는 앱의 app.yaml 파일에 지정됩니다. 부 버전 번호('Y')는 앱의 각 버전이 App Engine에 업로드될 때 자동으로 설정됩니다. 개발용 웹 서버에서 부 버전은 항상 '1'입니다.

  • AUTH_DOMAIN: Users API로 사용자를 인증하는 데 사용되는 도메인입니다. appspot.com에서 호스팅되는 앱의 AUTH_DOMAINgmail.com이며, 모든 Google 계정을 허용합니다. 커스텀 도메인에서 호스팅되는 앱의 AUTH_DOMAIN은 커스텀 도메인과 같습니다.

  • INSTANCE_ID: 요청을 처리하는 프런트엔드 인스턴스의 인스턴스 ID를 포함합니다. ID는 16진수 문자열입니다(예: 00c61b117c7f7fd0ce9e1325a04b8f0df30deaaf). 로그인한 관리자는 https://INSTANCE_ID-dot-VERSION_ID-dot-SERVICE_ID-dot-PROJECT_ID.REGION_ID.r.appspot.com URL에서 ID를 사용할 수 있습니다. 그러면 요청이 해당 프런트엔드 인스턴스로 라우팅됩니다. 인스턴스가 요청을 처리할 수 없는 경우 즉시 503을 반환합니다.

다음 환경 변수는 WSGI 및 CGI 표준의 일부이며, App Engine에서 특별한 동작을 보입니다.

  • SERVER_SOFTWARE: 개발용 웹 서버에서 이 값은 'Development/X.Y'입니다. 여기서 'X.Y'는 런타임의 버전입니다. App Engine에서 실행할 때는 이 값이 'Google App Engine/X.Y.Z'입니다.

WSGI 또는 CGI 표준에 따라 추가적인 환경 변수가 설정됩니다. 이러한 변수에 대한 자세한 내용은 각각 WSGI 표준 또는 CGI 표준을 참조하세요.

app.yaml 파일에서 환경 변수를 설정할 수도 있습니다.

env_variables:
  DJANGO_SETTINGS_MODULE: 'myapp.settings'

다음 webapp2 요청 핸들러는 브라우저에서 애플리케이션에 표시되는 모든 환경 변수를 표시합니다.

class PrintEnvironmentHandler(webapp2.RequestHandler):
    def get(self):
        self.response.headers["Content-Type"] = "text/plain"
        for key, value in os.environ.iteritems():
            self.response.out.write("{} = {}\n".format(key, value))

요청 ID

요청 당시 요청별로 고유한 요청 ID를 저장할 수 있습니다. 나중에 이 요청 ID를 사용하여 Cloud Logging에서 요청의 로그를 찾을 수 있습니다.

다음 샘플 코드에서는 요청 컨텍스트에서 요청 ID를 가져오는 방법을 보여줍니다.

class RequestIdHandler(webapp2.RequestHandler):
    def get(self):
        self.response.headers["Content-Type"] = "text/plain"
        request_id = os.environ.get("REQUEST_LOG_ID")
        self.response.write("REQUEST_LOG_ID={}".format(request_id))

HTTPS 연결 강제 적용

보안상의 이유로 모든 애플리케이션은 클라이언트가 https를 통해 연결하도록 권장해야 합니다. 브라우저에서 특정 페이지 또는 전체 도메인에 대해 http보다 https를 우선 사용하도록 지시하려면 응답에 Strict-Transport-Security 헤더를 설정합니다. 예를 들면 다음과 같습니다.

Strict-Transport-Security: max-age=31536000; includeSubDomains
앱에서 제공하는 모든 정적 콘텐츠에 이 헤더를 설정하려면 앱의 정적 파일 및 디렉터리 핸들러에 헤더를 추가합니다.

코드에서 생성된 응답에 이 헤더를 설정하려면 flask-talisman 라이브러리를 사용합니다.

비동기 백그라운드 작업 처리

백그라운드 작업은 HTTP 응답을 전송한 후에 앱이 요청에 수행하는 작업을 의미합니다. 앱에서 백그라운드 작업을 수행하지 못하게 하고 코드를 검토하여 응답을 전달하기 전에 모든 비동기 작업이 완료되었는지 확인합니다.

장기 실행 작업의 경우 Cloud Tasks를 사용하는 것이 좋습니다. Cloud Tasks를 사용하면 HTTP 요청이 오랫동안 유지되며 비동기 작업이 종료된 후에만 응답을 반환합니다.