Remote API로 App Engine에 액세스

리전 ID

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

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

Remote API 라이브러리를 사용하면 모든 Python 클라이언트가 App Engine 애플리케이션에 제공되는 서비스에 액세스할 수 있습니다.

예를 들어 App Engine 애플리케이션이 Datastore 또는 Google Cloud Storage를 사용하는 경우 Python 클라이언트는 Remote API를 사용하여 스토리지 리소스에 액세스 할 수 있습니다.

Remote API를 사용하여 로컬 머신에서 실행되는 앱 또는 로컬 대화형 Remote API 셸에서 애플리케이션의 데이터 저장소에 액세스할 수 있습니다. Remote API는 실제 서비스와 상호작용하므로 이 액세스로 할당량 및 청구 가능한 리소스가 사용됩니다.

앱에서 Remote API 액세스 사용 설정

애플리케이션에서 Remote API를 사용 설정하는 가장 쉬운 방법은 앱의 app.yaml 파일에서 builtins 지시문을 사용하는 것입니다. URL은 기본 URL /_ah/remote_api/를 지정합니다. 물론 같은 파일의 url 지시문을 대신 사용하여 다른 URL을 지정할 수도 있습니다.

builtin

app.yaml 파일의 builtins 지시문을 사용하면 기본 URL /_ah/remote_api에서 Remote API를 사용할 수 있습니다.

runtime: python27
api_version: 1
threadsafe: true

builtins:
- remote_api: on

URL

app.yamlurl 지시문을 사용하면 Remote API와 함께 사용할 다른 URL을 지정할 수 있습니다.

- url: /some-URL/*
  script: google.appengine.ext.remote_api.handler.application

이렇게 변경한 후 App Engine에 애플리케이션을 배포해야 합니다.

Remote API 셸 사용

Python SDK에는 애플리케이션이 사용하는 App Engine 서비스에서 Python 명령어를 호출할 수 있는 Remote API 셸이 포함되어 있습니다. 이 셸은 App Engine에 앱을 업로드하는 데 사용하는 사용자 인증 정보를 자동으로 사용하므로 추가 인증을 제공하지 않아도 됩니다.

Remote API 셸을 시작하려면 다음 단계를 따르세요.

  1. 로컬 머신의 터미널 창에서 다음 명령어를 호출합니다.

    SDK-INSTALL-DIRECTORY/remote_api_shell.py -s YOUR-PROJECT-ID. REGION_ID.r.appspot.com

    [SDK-INSTALL-DIRECTORY]에는 Python용 App Engine SDK에 대한 경로를, [YOUR-PROJECT-ID]에는 프로젝트 ID를 입력합니다.

  2. 표시되는 대화형 셸에서 실행할 Python 명령어를 호출합니다. 예를 들어 애플리케이션이 Datastore를 사용하는 경우 다음 ndb 쿼리를 호출하여 10개의 레코드를 가져올 수 있습니다.

     >>> from google.appengine.ext import ndb
     >>>
     >>> # Fetch 10 keys from the datastore
     >>> ndb.Query().fetch(10, keys_only=True)
    

로컬 클라이언트에서 Remote API 사용

로컬 애플리케이션에서 Remote API를 사용하여 App Engine에서 실행되는 앱이 사용하는 서비스에 액세스할 수도 있습니다.

로컬 애플리케이션에서 Remote API를 사용하려면 다음 단계를 따르세요.

  1. Remote API를 사용 설정합니다.

  2. Python 디렉터리의 PYTHONPATH 환경 변수를 내보냅니다. 예를 들면 다음과 같습니다.

     export PYTHONPATH=/usr/somedir/v3/bin/python2.7
    

    이 경로를 Python 위치의 실제 값으로 바꿉니다.

  3. Python용 App Engine SDK 위치를 PYTHONPATH에 추가합니다.

     export GAE_SDK_ROOT="/usr/local/home/mydir/google_appengine"
     export PYTHONPATH=${GAE_SDK_ROOT}:${PYTHONPATH}
    

    위에 나온 SDK 경로를 App Engine SDK의 실제 경로로 바꿉니다.

  4. 클라이언트 코드에서 dev_appserver를 가져오고 dev_appserver.fix_sys_path()를 호출하여 모든 App Engine SDK 모듈을 올바르게 가져오게 합니다.

    try:
        import dev_appserver
        dev_appserver.fix_sys_path()
  5. 애플리케이션에 다음 remote_api_stub 코드를 추가하여 코드의 프로젝트 ID를 전달합니다.

    remote_api_stub.ConfigureRemoteApiForOAuth(
        '{}.appspot.com'.format(project_id),
        '/_ah/remote_api')

    Remote API에 기본 URL /_ah/remote_api를 사용하지 않는 경우 사용 중인 URL을 반영하도록 위의 코드를 변경해야 합니다. remote_api_stub.ConfigureRemoteApiForOAuth에 대한 정의 및 문서는 SDK 파일 [SDK-INSTALL-DIRECTORY]/google/appengine/ext/remote_api/remote_api_stub.py을 참조하세요.

  6. 원하는 App Engine 서비스에 액세스하려면 필요한 App Engine 가져오기 및 Python 코드를 추가합니다. 다음 샘플 코드는 프로젝트의 데이터 저장소에 액세스합니다.

    
    import argparse
    
    try:
        import dev_appserver
        dev_appserver.fix_sys_path()
    except ImportError:
        print('Please make sure the App Engine SDK is in your PYTHONPATH.')
        raise
    
    from google.appengine.ext import ndb
    from google.appengine.ext.remote_api import remote_api_stub
    
    def main(project_id):
        remote_api_stub.ConfigureRemoteApiForOAuth(
            '{}.appspot.com'.format(project_id),
            '/_ah/remote_api')
    
        # List the first 10 keys in the datastore.
        keys = ndb.Query().fetch(10, keys_only=True)
    
        for key in keys:
            print(key)
    
    if __name__ == '__main__':
        parser = argparse.ArgumentParser(
            description=__doc__,
            formatter_class=argparse.RawDescriptionHelpFormatter)
        parser.add_argument('project_id', help='Your Project ID.')
    
        args = parser.parse_args()
    
        main(args.project_id)
  7. App Engine에 애플리케이션이 배포되면 Remote API 클라이언트를 시작합니다.

     python your-client.py YOUR-PROJECT-ID
    

    your-client.py를 클라이언트 모듈로 바꾸고 YOUR-PROJECT-ID를 프로젝트 ID로 바꿉니다. 이는 클라이언트가 client.py 코드 샘플에 따라 프로젝트 ID를 명령줄 입력으로 수락한다고 가정합니다.

제한사항 및 권장사항

remote_api 모듈은 기본 App Engine Datastore와 완전히 동일하게 작동되도록 하기 위해 최대한으로 시도합니다. 즉, 경우에 따라 효율성이 떨어지는 작업을 수행하기도 한다는 의미입니다. 따라서 remote_api를 사용할 때는 다음 사항에 주의하시기 바랍니다.

모든 Datastore 요청에 왕복이 필요함

HTTP를 통해 Datastore에 액세스하는 경우 로컬에서 액세스할 때보다 오버헤드와 지연 시간이 좀 더 늘어날 수 있습니다. 시간을 단축하고 부하를 줄이기 위해서는 get과 put 작업을 일괄 처리하고 쿼리 항목을 일괄적으로 가져와 왕복 횟수를 제한해야 합니다. 일괄 작업은 단일 Datastore 작업으로 간주되기 때문에 이 방식은 remote_api뿐만 아니라 Datastore를 사용할 때도 대체로 유용합니다. 예를 들어 다음을 수행하는 대신,

for key in keys:
  rec = key.get()
  rec.foo = bar
  rec.put()

다음을 수행할 수 있습니다.

records = ndb.get_multi(keys)
for rec in records:
  rec.foo = bar
  ndb.put_multi(records)

두 예시 모두 효과가 동일합니다. 하지만 첫 번째 예시는 항목마다 두 번의 왕복이 필요하지만, 두 번째 예시는 총 두 번의 왕복만 필요합니다.

remote_api 요청 시 할당량이 사용됨

remote_api가 HTTP를 통해 작동하므로 Datastore를 호출할 때마다 HTTP 요청에 대한 할당량(바이트 단위 입출력)뿐만 아니라 일반적으로 예상되는 Datastore 할당량이 차감됩니다. 일괄 업데이트에 remote_api를 사용하는 경우 이 점을 염두에 두시기 바랍니다.

1MB API 제한 적용

기본 실행에서와 마찬가지로 API 요청 및 응답에는 1MB 제한이 계속 적용됩니다. 특히 항목 크기가 큰 경우 이 한도가 초과되지 않도록 한 번에 수행하는 fetch 또는 put 작업 횟수를 제한해야 할 수 있습니다. 이 경우 왕복을 최소화하는 접근 방식과 상충되기 때문에 요청 또는 응답 크기의 제한을 넘지 않는 범위 내에서 가장 큰 배치를 사용하는 것이 좋습니다. 그러나 대부분의 항목에서는 이 제한이 문제가 되지 않습니다.

쿼리 반복 방지

Datastore 액세스가 사용되는 한 가지 일반적인 패턴은 다음과 같습니다.

q = MyModel.query()
for entity in q:
  # Do something with entity

이를 수행하면 SDK는 Datastore의 항목을 20개 단위의 배치로 가져오며 기존의 항목이 소모될 때마다 새 배치를 가져옵니다. 각 배치는 remote_api를 통해 별도 요청으로 가져와야 하기 때문에 효율성이 떨어질 수밖에 없습니다. 대신 remote_api는 오프셋 기능을 사용하여 각 배치별로 완전히 새로운 쿼리를 실행하므로 결과의 정확성이 높아집니다.

필요한 항목 수를 알고 있다면 필요한 숫자를 묻는 방식으로 한 요청에서 전체 가져오기를 수행할 수 있습니다.

entities = MyModel.query().fetch(100)
for entity in entities:
  # Do something with entity

원하는 항목 수를 모르는 경우에는 커서를 사용하여 큰 결과 집합을 효율적으로 반복할 수 있습니다. 이렇게 하면 일반적인 Datastore 쿼리에 적용되는 1,000개 항목 제한을 피할 수 있습니다.

트랜잭션의 효율성이 떨어짐

remote_api를 통해 트랜잭션을 구현하기 위해 트랜잭션 내에서 가져온 항목에 대한 정보와 트랜잭션 내에서 배치 또는 삭제한 항목의 사본이 축적됩니다. 트랜잭션이 커밋되면 이 모든 정보가 App Engine 서버로 전송되며, 이때 트랜잭션에 사용된 모든 항목을 다시 가져와 변경되지 않았는지 확인한 다음 트랜잭션에 적용된 모든 변경사항에 대해 put 및 delete 작업을 수행한 후 커밋합니다. 충돌이 발생하면 서버가 트랜잭션을 롤백하고 클라이언트 측에 이를 알립니다. 그러면 클라이언트가 전체 프로세스를 처음부터 다시 반복해야 합니다.

이 접근 방식은 제대로 작동하며 트랜잭션에서 제공된 기능을 로컬 Datastore에 정확하게 복제하지만, 다소 비효율적입니다. 따라서 어떤 경우에도 꼭 필요한 경우에만 트랜잭션을 사용해야 하며 효율성을 위해 실행할 트랜잭션의 수와 복잡성을 제한해야 합니다.