이 가이드에서는 App Engine Blobstore에서 Cloud Storage로 마이그레이션하는 방법을 설명합니다.
Cloud Storage는 동영상 또는 이미지 파일과 같은 대용량 데이터 객체(blob)를 처리하고 사용자가 큰 데이터 파일을 업로드할 수 있다는 점에서 App Engine Blobstore와 유사합니다. App Engine 기존 번들 서비스를 통해서만 App Engine Blobstore에 액세스할 수 있지만 Cloud Storage는 Cloud 클라이언트 라이브러리를 통해 액세스할 수 있는 독립형 Google Cloud 제품입니다. Cloud Storage는 앱에 최신 객체 스토리지 솔루션을 제공하고 나중에 유연하게 Cloud Run 또는 다른 Google Cloud 앱 호스팅 플랫폼으로 마이그레이션할 수 있도록 지원합니다.
2016년 11월 이후에 생성된 Google Cloud 프로젝트의 경우 Blobstore는 백그라운드에서 Cloud Storage 버킷을 사용합니다. 즉, 앱을 Cloud Storage로 마이그레이션할 때 기존 Cloud Storage 버킷의 모든 기존 객체와 권한은 변경되지 않습니다. Cloud Storage용 Cloud 클라이언트 라이브러리를 사용하여 이러한 기존 버킷에 액세스할 수도 있습니다.
주요 차이점 및 유사점
Cloud Storage는 다음 Blobstore 종속 항목과 제한사항을 제외합니다.
- Python 2용 Blobstore API는 웹앱에 종속됩니다.
- Python 3용 Blobstore API는 유틸리티 클래스를 사용하여 Blobstore 핸들러를 사용합니다.
- Blobstore의 경우 Blobstore에 업로드할 수 있는 최대 파일 수는 500개입니다. Cloud Storage 버킷에서 만들 수 있는 객체 수에는 제한이 없습니다.
Cloud Storage는 다음을 지원하지 않습니다.
- Blobstore 핸들러 클래스
- Blobstore 객체
Cloud Storage와 App Engine Blobstore의 유사점은 다음과 같습니다.
- 런타임 환경에서 대용량 데이터 객체를 읽고 쓸 수 있을 뿐만 아니라 영화, 이미지 또는 기타 정적 콘텐츠와 같은 큰 정적 데이터 객체를 저장하고 제공할 수 있습니다. Cloud Storage의 객체 크기 한도는 5TiB입니다.
- Cloud Storage 버킷에 객체를 저장할 수 있습니다.
- 무료 등급이 있습니다.
시작하기 전에
- Cloud Storage 가격 책정과 할당량을 검토하고 이해해야 합니다.
- Cloud Storage는 유료 서비스이며 데이터의 스토리지 클래스와 버킷 위치에 따라 데이터 스토리지에 대한 자체 가격이 적용됩니다.
- Cloud Storage 할당량에는 App Engine 요청 할당량에 영향을 줄 수 있는 App Engine Blobstore 할당량 및 한도와 몇 가지 차이점이 있습니다.
- Blobstore를 사용하는 기존 Python 2 또는 Python 3 App Engine 앱이 있습니다.
- 이 가이드의 예시에서는 Flask 프레임워크를 사용하여 Cloud Storage로 마이그레이션하는 앱을 보여줍니다. Cloud Storage로 마이그레이션할 때
webapp2
유지를 포함하여 모든 웹 프레임워크를 사용할 수 있습니다.
개요
App Engine Blobstore에서 Cloud Storage로 마이그레이션하는 프로세스는 개략적으로 다음 단계로 구성됩니다.
- 구성 파일 업데이트
- Python 앱 업데이트:
- 웹 프레임워크 업데이트
- Cloud Storage 가져오기 및 초기화
- Blobstore 핸들러 업데이트
- 선택사항: Cloud NDB 또는 App Engine NDB를 사용하는 경우 데이터 모델 업데이트
- 앱 테스트 및 배포
구성 파일 업데이트
애플리케이션 코드가 Blobstore에서 Cloud Storage로 이동하도록 수정하기 전에 Cloud Storage 라이브러리를 사용하도록 구성 파일을 업데이트합니다.
app.yaml
파일을 업데이트합니다. 사용 중인 Python 버전에 해당하는 안내를 따르세요.Python 2
Python 2 앱의 경우:
handlers
섹션과libraries
섹션의 불필요한 웹앱 종속 항목을 삭제합니다.- Cloud 클라이언트 라이브러리를 사용하는 경우 최신 버전의
grpcio
및setuptools
라이브러리를 추가합니다. - Cloud Storage에 필요하므로
ssl
라이브러리를 추가합니다.
다음은 변경사항이 적용된
app.yaml
파일의 예시입니다.runtime: python27 threadsafe: yes api_version: 1 handlers: - url: /.* script: main.app libraries: - name: grpcio version: latest - name: setuptools version: latest - name: ssl version: latest
Python 3
Python 3 앱의 경우
runtime
요소를 제외한 모든 줄을 삭제합니다. 예를 들면 다음과 같습니다.runtime: python310 # or another support version
Python 3 런타임은 라이브러리를 자동으로 설치하므로 이전 Python 2 런타임의 기본 제공 라이브러리를 지정할 필요가 없습니다. Cloud Storage로 마이그레이션할 때 Python 3 앱에서 다른 기존 번들 서비스를 사용하는 경우
app.yaml
파일을 그대로 둡니다.requirements.txt
파일을 업데이트합니다. 사용 중인 Python 버전에 해당하는 안내를 따르세요.Python 2
Cloud Storage용 Cloud 클라이언트 라이브러리를
requirements.txt
파일의 종속 항목 목록에 추가합니다.google-cloud-storage
그런 다음
pip install -t lib -r requirements.txt
를 실행하여 앱에 사용 가능한 라이브러리 목록을 업데이트합니다.Python 3
Cloud Storage용 Cloud 클라이언트 라이브러리를
requirements.txt
파일의 종속 항목 목록에 추가합니다.google-cloud-storage
App Engine은 Python 3 런타임에서 앱 배포 중에 이러한 종속 항목을 자동으로 설치하므로,
lib
폴더가 있는 경우 이를 삭제하세요.Python 2 앱의 경우 앱에서 기본 제공 또는 복사된 라이브러리를 사용한다면
appengine_config.py
파일에 해당 경로를 지정해야 합니다.import pkg_resources from google.appengine.ext import vendor # Set PATH to your libraries folder. PATH = 'lib' # Add libraries installed in the PATH folder. vendor.add(PATH) # Add libraries to pkg_resources working set to find the distribution. pkg_resources.working_set.add_entry(PATH)
Python 앱 업데이트
구성 파일을 수정한 후 Python 앱을 업데이트합니다.
Python 2 웹 프레임워크 업데이트
webapp2
프레임워크를 사용하는 Python 2 앱의 경우 오래된 webapp2
프레임워크에서 마이그레이션하는 것이 좋습니다. Python 2 지원 종료 날짜는 런타임 지원 일정을 참조하세요.
Flask, Django 또는 WSGI와 같은 다른 웹 프레임워크로 마이그레이션할 수 있습니다. Cloud Storage는 webapp2
에 대한 종속 항목을 제외하고 Blobstore 핸들러는 지원되지 않으므로 다른 웹앱 관련 라이브러리를 삭제하거나 바꿀 수 있습니다.
webapp2
를 계속 사용하기로 선택한 경우 이 가이드의 예시에서 Flask와 함께 Cloud Storage를 사용합니다.
Cloud Storage 외에도 Google Cloud 서비스를 사용하거나 최신 런타임 버전에 액세스하려면 앱을 Python 3 런타임으로 업그레이드하는 것이 좋습니다. 자세한 내용은 Python 2에서 Python 3로 마이그레이션 개요를 참조하세요.
Cloud Storage 가져오기 및 초기화
가져오기 및 초기화 줄을 업데이트하여 애플리케이션 파일을 수정합니다.
다음과 같은 Blobstore 가져오기 문을 삭제합니다.
import webapp2 from google.appengine.ext import blobstore from google.appengine.ext.webapp import blobstore_handlers
다음과 같이 Cloud Storage 및 Google 인증 라이브러리에 대한 가져오기 문을 추가합니다.
import io from flask import (Flask, abort, redirect, render_template, request, send_file, url_for) from google.cloud import storage import google.auth
Cloud Storage의 Blobstore에 사용된 프로젝트 ID와 같은 프로젝트 ID를 가져오려면 Google 인증 라이브러리가 필요합니다. 앱에 해당하는 경우 Cloud NBD와 같은 다른 라이브러리를 가져옵니다.
Cloud Storage용 새 클라이언트를 만들고 Blobstore에서 사용되는 버킷을 지정합니다. 예를 들면 다음과 같습니다.
gcs_client = storage.Client() _, PROJECT_ID = google.auth.default() BUCKET = '%s.appspot.com' % PROJECT_ID
2016년 11월 이후의 Google Cloud 프로젝트에서 Blobstore는 앱의 URL에 따라 이름이 지정된 Cloud Storage 버킷에 쓰고
PROJECT_ID.appspot.com
형식을 따릅니다. Google 인증을 사용하여 프로젝트 ID를 가져와 Blobstore에 blob을 저장하는 데 사용되는 Cloud Storage 버킷을 지정합니다.
Blobstore 핸들러 업데이트
Cloud Storage는 Blobstore 업로드 및 다운로드 핸들러를 지원하지 않으므로 Cloud Storage 기능, io
표준 라이브러리 모듈, 웹 프레임워크, Python 유틸리티를 조합하여 Cloud Storage의 객체(blob)를 업로드 및 다운로드해야 합니다.
다음은 Flask를 웹 프레임워크 예시로 사용하여 Blobstore 핸들러를 업데이트하는 방법을 보여줍니다.
Blobstore 업로드 핸들러 클래스를 Flask의 업로드 함수로 바꿉니다. 사용 중인 Python 버전에 해당하는 안내를 따르세요.
Python 2
Python 2의 Blobstore 핸들러는 다음 Blobstore 예시와 같은
webapp2
클래스입니다.class UploadHandler(blobstore_handlers.BlobstoreUploadHandler): 'Upload blob (POST) handler' def post(self): uploads = self.get_uploads() blob_id = uploads[0].key() if uploads else None store_visit(self.request.remote_addr, self.request.user_agent, blob_id) self.redirect('/', code=307) ... app = webapp2.WSGIApplication([ ('/', MainHandler), ('/upload', UploadHandler), ('/view/([^/]+)?', ViewBlobHandler), ], debug=True)
Cloud Storage를 사용하려면 다음 안내를 따르세요.
- 웹앱 업로드 클래스를 Flask 업로드 함수로 바꿉니다.
- 업로드 핸들러와 라우팅을 라우팅으로 데코레이션된 Flask
POST
메서드로 바꿉니다.
업데이트된 코드 샘플:
@app.route('/upload', methods=['POST']) def upload(): 'Upload blob (POST) handler' fname = None upload = request.files.get('file', None) if upload: fname = secure_filename(upload.filename) blob = gcs_client.bucket(BUCKET).blob(fname) blob.upload_from_file(upload, content_type=upload.content_type) store_visit(request.remote_addr, request.user_agent, fname) return redirect(url_for('root'), code=307)
업데이트된 Cloud Storage 코드 샘플에서 앱은 이제
blob_id
대신 객체 이름(fname
)으로 객체 아티팩트를 식별합니다. 라우팅은 애플리케이션 파일 하단에서도 발생합니다.업로드된 객체를 가져오기 위해 Blobstore의
get_uploads()
메서드가 Flask의request.files.get()
메서드로 대체됩니다. Flask에서는secure_filename()
메서드를 사용하여 파일에/
와 같은 경로 문자가 없는 이름을 가져오고,gcs_client.bucket(BUCKET).blob(fname)
을 사용하여 버킷 이름과 객체 이름을 지정하여 객체를 식별합니다.업데이트된 예시와 같이 Cloud Storage
upload_from_file()
호출에서 업로드를 수행합니다.Python 3
Python 3용 Blobstore의 업로드 핸들러 클래스는 유틸리티 클래스이며 다음 Blobstore 예시와 같이 WSGI
environ
사전을 입력 매개변수로 사용해야 합니다.class UploadHandler(blobstore.BlobstoreUploadHandler): 'Upload blob (POST) handler' def post(self): uploads = self.get_uploads(request.environ) if uploads: blob_id = uploads[0].key() store_visit(request.remote_addr, request.user_agent, blob_id) return redirect('/', code=307) ... @app.route('/upload', methods=['POST']) def upload(): """Upload handler called by blobstore when a blob is uploaded in the test.""" return UploadHandler().post()
Cloud Storage를 사용하려면 Blobstore의
get_uploads(request.environ)
메서드를 Flask의request.files.get()
메서드로 바꿉니다.업데이트된 코드 샘플:
@app.route('/upload', methods=['POST']) def upload(): 'Upload blob (POST) handler' fname = None upload = request.files.get('file', None) if upload: fname = secure_filename(upload.filename) blob = gcs_client.bucket(BUCKET).blob(fname) blob.upload_from_file(upload, content_type=upload.content_type) store_visit(request.remote_addr, request.user_agent, fname) return redirect(url_for('root'), code=307)
업데이트된 Cloud Storage 코드 샘플에서 앱은 이제
blob_id
대신 객체 이름(fname
)으로 객체 아티팩트를 식별합니다. 라우팅은 애플리케이션 파일 하단에서도 발생합니다.업로드된 객체를 가져오기 위해 Blobstore의
get_uploads()
메서드가 Flask의request.files.get()
메서드로 대체됩니다. Flask에서는secure_filename()
메서드를 사용하여 파일에/
와 같은 경로 문자가 없는 이름을 가져오고,gcs_client.bucket(BUCKET).blob(fname)
을 사용하여 버킷 이름과 객체 이름을 지정하여 객체를 식별합니다.Cloud Storage
upload_from_file()
메서드는 업데이트된 예시와 같이 업로드를 수행합니다.Blobstore 다운로드 핸들러 클래스를 Flask의 다운로드 함수로 바꿉니다. 사용 중인 Python 버전에 해당하는 안내를 따르세요.
Python 2
다음 다운로드 핸들러 예시에서는 webapp2를 사용하는
BlobstoreDownloadHandler
클래스를 사용하는 방법을 보여줍니다.class ViewBlobHandler(blobstore_handlers.BlobstoreDownloadHandler): 'view uploaded blob (GET) handler' def get(self, blob_key): self.send_blob(blob_key) if blobstore.get(blob_key) else self.error(404) ... app = webapp2.WSGIApplication([ ('/', MainHandler), ('/upload', UploadHandler), ('/view/([^/]+)?', ViewBlobHandler), ], debug=True)
Cloud Storage를 사용하려면 다음 안내를 따르세요.
- Cloud Storage의
download_as_bytes()
메서드를 사용하도록 Blobstore의send_blob()
메서드를 업데이트합니다. - 라우팅을 webapp2에서 Flask로 변경합니다.
업데이트된 코드 샘플:
@app.route('/view/<path:fname>') def view(fname): 'view uploaded blob (GET) handler' blob = gcs_client.bucket(BUCKET).blob(fname) try: media = blob.download_as_bytes() except exceptions.NotFound: abort(404) return send_file(io.BytesIO(media), mimetype=blob.content_type)
업데이트된 Cloud Storage 코드 샘플에서 Flask는 Flask 함수의 경로를 데코레이션하고
'/view/<path:fname>'
를 사용하여 객체를 식별합니다. Cloud Storage는 객체 이름과 버킷 이름으로blob
객체를 식별하고 Blobstore의send_blob
메서드를 사용하는 대신download_as_bytes()
메서드를 사용하여 객체를 바이트로 다운로드합니다. 아티팩트가 발견되지 않으면 앱에서 HTTP404
오류를 반환합니다.Python 3
Python 3용 Blobstore의 다운로드 핸들러 클래스는 업로드 핸들러와 마찬가지로 유틸리티 클래스이며 다음 Blobstore 예시와 같이 WSGI
environ
사전을 입력 매개변수로 사용해야 합니다.class ViewBlobHandler(blobstore.BlobstoreDownloadHandler): 'view uploaded blob (GET) handler' def get(self, blob_key): if not blobstore.get(blob_key): return "Photo key not found", 404 else: headers = self.send_blob(request.environ, blob_key) # Prevent Flask from setting a default content-type. # GAE sets it to a guessed type if the header is not set. headers['Content-Type'] = None return '', headers ... @app.route('/view/<blob_key>') def view_photo(blob_key): """View photo given a key.""" return ViewBlobHandler().get(blob_key)
Cloud Storage를 사용하려면 Blobstore의
send_blob(request.environ, blob_key)
을 Cloud Storage의blob.download_as_bytes()
메서드로 바꿉니다.업데이트된 코드 샘플:
@app.route('/view/<path:fname>') def view(fname): 'view uploaded blob (GET) handler' blob = gcs_client.bucket(BUCKET).blob(fname) try: media = blob.download_as_bytes() except exceptions.NotFound: abort(404) return send_file(io.BytesIO(media), mimetype=blob.content_type)
업데이트된 Cloud Storage 코드 샘플에서
blob_key
는fname
으로 바뀌고 Flask는'/view/<path:fname>'
URL을 사용하여 객체를 식별합니다.gcs_client.bucket(BUCKET).blob(fname)
메서드는 파일 이름과 버킷 이름을 찾는 데 사용됩니다. Cloud Storage의download_as_bytes()
메서드는 Blobstore의send_blob()
메서드를 사용하는 대신 객체를 바이트로 다운로드합니다.- Cloud Storage의
앱에서 기본 핸들러를 사용하는 경우 Flask에서
MainHandler
클래스를root()
함수로 바꿉니다. 사용 중인 Python 버전에 해당하는 안내를 따르세요.Python 2
다음은 Blobstore의
MainHandler
클래스를 사용하는 예시입니다.class MainHandler(BaseHandler): 'main application (GET/POST) handler' def get(self): self.render_response('index.html', upload_url=blobstore.create_upload_url('/upload')) def post(self): visits = fetch_visits(10) self.render_response('index.html', visits=visits) app = webapp2.WSGIApplication([ ('/', MainHandler), ('/upload', UploadHandler), ('/view/([^/]+)?', ViewBlobHandler), ], debug=True)
Cloud Storage를 사용하려면 다음 안내를 따르세요.
- Flask에서 자동으로 라우팅을 처리하므로
MainHandler(BaseHandler)
클래스를 삭제합니다. - Flask로 Blobstore 코드를 단순화합니다.
- 끝에 있는 웹앱 라우팅을 삭제합니다.
업데이트된 코드 샘플:
@app.route('/', methods=['GET', 'POST']) def root(): 'main application (GET/POST) handler' context = {} if request.method == 'GET': context['upload_url'] = url_for('upload') else: context['visits'] = fetch_visits(10) return render_template('index.html', **context)
Python 3
Flask를 사용한 경우
MainHandler
클래스는 없지만 blobstore를 사용하면 Flask 루트 함수를 업데이트해야 합니다. 다음 예시에서는blobstore.create_upload_url('/upload')
함수를 사용합니다.@app.route('/', methods=['GET', 'POST']) def root(): 'main application (GET/POST) handler' context = {} if request.method == 'GET': context['upload_url'] = blobstore.create_upload_url('/upload') else: context['visits'] = fetch_visits(10) return render_template('index.html', **context)
Cloud Storage를 사용하려면
blobstore.create_upload_url('/upload')
함수를 Flask의url_for()
메서드로 바꿔upload()
함수의 URL을 가져옵니다.업데이트된 코드 샘플:
@app.route('/', methods=['GET', 'POST']) def root(): 'main application (GET/POST) handler' context = {} if request.method == 'GET': context['upload_url'] = url_for('upload') # Updated to use url_for else: context['visits'] = fetch_visits(10) return render_template('index.html', **context)
- Flask에서 자동으로 라우팅을 처리하므로
앱 테스트 및 배포
로컬 개발 서버를 사용하면 앱이 실행되는지 테스트할 수 있지만 모든 Cloud Storage 요청을 인터넷을 통해 실제 Cloud Storage 버킷에 보내야 하므로 새 버전을 배포할 때까지 Cloud Storage를 테스트할 수 없습니다. 애플리케이션을 로컬에서 실행하는 방법은 애플리케이션 테스트 및 배포를 참조하세요. 그런 다음 새 버전을 배포하여 앱이 이전과 동일하게 표시되는지 확인합니다.
App Engine NDB 또는 Cloud NDB를 사용하는 앱
앱에서 App Engine NDB 또는 Cloud NDB를 사용하여 Blobstore 관련 속성을 포함하는 경우 Datastore 데이터 모델을 업데이트해야 합니다.
데이터 모델 업데이트
NDB의 BlobKey
속성은 Cloud Storage에서 지원되지 않으므로 NDB, 웹 프레임워크 또는 다른 곳의 기본 제공 등가 항목을 사용하도록 Blobstore 관련 줄을 수정해야 합니다.
데이터 모델을 업데이트하려면 다음 안내를 따르세요.
다음과 같이 데이터 모델에서
BlobKey
를 사용하는 줄을 찾습니다.class Visit(ndb.Model): 'Visit entity registers visitor IP address & timestamp' visitor = ndb.StringProperty() timestamp = ndb.DateTimeProperty(auto_now_add=True) file_blob = ndb.BlobKeyProperty()
ndb.BlobKeyProperty()
를ndb.StringProperty()
로 바꿉니다.class Visit(ndb.Model): 'Visit entity registers visitor IP address & timestamp' visitor = ndb.StringProperty() timestamp = ndb.DateTimeProperty(auto_now_add=True) file_blob = ndb.StringProperty() # Modified from ndb.BlobKeyProperty()
마이그레이션 중에 App Engine NDB에서 Cloud NDB로 업그레이드하는 경우 Python 컨텍스트 관리자를 사용하도록 NDB 코드를 리팩터링하는 방법에 대한 안내는 Cloud NDB 마이그레이션 가이드를 참조하세요.
Datastore 데이터 모델의 하위 호환성
이전 섹션에서 ndb.BlobKeyProperty
를 ndb.StringProperty
로 바꾸면 앱이 이전 버전과 호환되지 않게 됩니다. 즉, 앱에서 Blobstore에서 만든 이전 항목을 처리할 수 없습니다. 이전 데이터를 유지해야 하는 경우 ndb.BlobKeyProperty
필드를 업데이트하는 대신 새 Cloud Storage 항목의 필드를 추가로 만들고 데이터를 정규화하는 함수를 만듭니다.
이전 섹션의 예시에서 다음과 같이 변경합니다.
데이터 모델을 정의할 때 별도 속성 필드 2개를 만듭니다.
file_blob
속성을 사용하여 Blobstore에서 만든 객체를 식별하고file_gcs
속성을 사용하여 Cloud Storage에서 만든 객체를 식별합니다.class Visit(ndb.Model): 'Visit entity registers visitor IP address & timestamp' visitor = ndb.StringProperty() timestamp = ndb.DateTimeProperty(auto_now_add=True) file_blob = ndb.BlobKeyProperty() # backwards-compatibility file_gcs = ndb.StringProperty()
다음과 같이 신규 방문을 참조하는 줄을 찾습니다.
def store_visit(remote_addr, user_agent, upload_key): 'create new Visit entity in Datastore' with ds_client.context(): Visit(visitor='{}: {}'.format(remote_addr, user_agent), file_blob=upload_key).put()
file_gcs
가 최근 항목에 사용되도록 코드를 변경합니다. 예를 들면 다음과 같습니다.def store_visit(remote_addr, user_agent, upload_key): 'create new Visit entity in Datastore' with ds_client.context(): Visit(visitor='{}: {}'.format(remote_addr, user_agent), file_gcs=upload_key).put() # change file_blob to file_gcs for new requests
데이터를 정규화하는 새 함수를 만듭니다. 다음 예시에서는 추출, 변환, 로드(ETL)를 사용하여 모든 방문을 반복하고, 방문자 및 타임스탬프 데이터를 가져와
file_gcs
또는file_gcs
가 있는지 확인합니다.def etl_visits(visits): return [{ 'visitor': v.visitor, 'timestamp': v.timestamp, 'file_blob': v.file_gcs if hasattr(v, 'file_gcs') \ and v.file_gcs else v.file_blob } for v in visits]
fetch_visits()
함수를 참조하는 줄을 찾습니다.@app.route('/', methods=['GET', 'POST']) def root(): 'main application (GET/POST) handler' context = {} if request.method == 'GET': context['upload_url'] = url_for('upload') else: context['visits'] = fetch_visits(10) return render_template('index.html', **context)
etl_visits()
함수 내에서fetch_visits()
를 래핑합니다. 예를 들면 다음과 같습니다.@app.route('/', methods=['GET', 'POST']) def root(): 'main application (GET/POST) handler' context = {} if request.method == 'GET': context['upload_url'] = url_for('upload') else: context['visits'] = etl_visits(fetch_visits(10)) # etl_visits wraps around fetch_visits return render_template('index.html', **context)
예시
- Python 2 앱을 Cloud Storage로 마이그레이션하는 방법의 예시를 보려면 GitHub의 Python 2용 Blobstore 코드 샘플 및 Cloud Storage 코드 샘플을 비교하세요.
- Python 3 앱을 Cloud Storage로 마이그레이션하는 방법의 예시를 보려면 GitHub의 Python 3용 Blobstore 코드 샘플 및 Cloud Storage 코드 샘플을 비교하세요.
다음 단계
- 실습 튜토리얼은 Python용 App Engine Blobstore에서 Cloud Storage로 마이그레이션 Codelab을 참조하세요.
- Cloud Storage에서 정적 파일을 저장 및 제공하는 방법 알아보기
- 자세한 내용은 Cloud Storage 문서를 참조하세요.