Google Play 개발자 API 권한 모델은 GCP IAM과 Play Console 두 레이어로 분리된다
·수정 3회
요약
- Google Play 개발자 API(androidpublisher)는 GCP 프로젝트의 SA로 인증받고, Play Console에서 부여된 권한으로 인가받는 2단계 모델이다.
- 둘은 별개 권한 시스템이고, "자동 링크 프로젝트(
api-숫자-숫자형식)"라는 자동 생성된 GCP 프로젝트가 두 세계를 연결한다. - SA만 만들어도, Play Console에서 invite + 권한 부여 + 앱 권한/계정 권한 모두 체크해야 비로소 API가 동작한다.
본문
두 세계와 한 개의 다리
┌────────────────────────────────────────┐ ┌────────────────────────────────────────┐
│ GCP (cloud.google.com) │ │ Play Console (play.google.com/console)│
│ │ │ │
│ Project: api-XXXX-XXXXXX │ │ Developer Account: XXXX...XXXXXX │
│ └── IAM │ │ ├── Apps: com.example.app │
│ └── Service Accounts │←──→│ └── Users and permissions │
│ └── sa-name@... │ │ └── sa-name@... (invited) │
│ └── Key (JSON) │ │ ├── App permissions │
│ │ │ └── Account permissions │
│ API: Google Play Android Developer │ │ │
│ (활성화 필요) │ │ │
└────────────────────────────────────────┘ └────────────────────────────────────────┘
↑ ↑
│ 인증 (SA JSON → OAuth2 token) │ 인가 (어떤 endpoint 호출 가능?)
└──────────────────┬───────────────────────────┘
│
androidpublisher.googleapis.com
호출 시 둘 다 통과해야 200
자동 링크 프로젝트 (api-XXXX-NNNNNN)
- Play Console에서 새 개발자 계정 생성 시 GCP에
api-<숫자>-<숫자>형식의 프로젝트가 자동 생성된다. - 이 프로젝트는 Play Console 전용으로 androidpublisher API 활성화가 기본으로 돼 있다.
- 자유롭게 다른 GCP 프로젝트를 직접 link할 수도 있지만, 자동 링크 프로젝트가 가장 무난하다.
- 자동 링크 프로젝트의 GCP IAM에 SA를 만들고, 그 SA email을 Play Console에서 invite하는 게 표준 흐름이다.
인증과 인가의 분리
인증 (Authentication) — "너 누구야?"
- SA JSON에서 OAuth2 access token 발급
- scope:
https://www.googleapis.com/auth/androidpublisher google.oauth2.service_account.Credentials→ token endpoint- 키가 valid하면 token 발급 성공 (이 단계에서 401 안 남)
인가 (Authorization) — "넌 뭘 할 수 있어?"
- Play Console에서 해당 SA에 부여된 권한으로 결정
- endpoint마다 요구 권한이 다르다 (
subscriptions.get≠voidedpurchases.list) - 인가 실패 시 401
permissionDenied또는 403Forbidden
Play Console 권한의 두 축
Play Console "Users and permissions"에서 SA 클릭 시 두 탭이 분리돼 있다:
앱 권한 (App permissions) — 특정 앱에 대한 권한
- 특정 앱에 대해 재무 데이터 보기 / 주문 및 구독 관리 등 개별 체크
- 앱별로 다르게 설정 가능
계정 권한 (Account permissions) — 개발자 계정 전체에 대한 권한
- 모든 앱에 공통으로 적용되는 권한
- 일부 endpoint(예:
purchases.subscriptions.get)는 계정 권한 레벨의 동일 항목까지 체크되어야 동작
같은 "재무 데이터 보기"라도 App 레벨에서 체크한 것과 Account 레벨에서 체크한 것이 Google 내부적으로 별개 스코프로 평가된다. (자세한 사례는 Play Console SA는 앱 권한만으로는 purchases.subscriptions.get이 401을 반환한다)
표준 등록 흐름
- GCP — Play Console과 연결된 자동 링크 프로젝트 또는 직접 link한 프로젝트에 SA 생성
- GCP IAM — SA에는 별도 IAM role 불필요 (API 호출은 SA identity 자체로 충분)
- GCP — Service Account Key 생성 (JSON 다운로드)
- Play Console → Users and permissions — SA email 입력해서 invite
- 앱 권한 탭 — 대상 앱 추가 + 필요한 권한 체크
- 계정 권한 탭 — 동일 권한 체크 (특히 재무/주문 관련)
- 변경사항 저장
- propagation 대기 (수 분 ~ 수 시간, intermittent 가능)
흔히 놓치는 함정
- GCP IAM 권한 ≠ Play Console 권한: GCP IAM에서 SA에
roles/owner를 줘도 Play API는 못 부른다. Play 권한은 Play Console에서만 부여된다. - API 활성화 별도: 자동 링크 프로젝트가 아닌 다른 GCP 프로젝트를 쓸 경우 "Google Play Android Developer API" 활성화를 수동으로 해줘야 한다.
- SA 키 회전 시 권한: 같은 SA의 키만 회전하면 권한은 그대로 유지된다. 그러나 새 SA로 교체하면 새 SA를 다시 invite 해야 한다.
- "Setup → API access" deprecated: 일부 계정에서는 이 메뉴가 사라졌다. 현대 UI는 Users and permissions로 통합되어 있고, 메뉴가 없다고 잘못된 게 아니다.
- Play Console과 다른 SA 사용처 혼동: Firebase Admin, GCS, Cloud Tasks 등 다른 GCP 서비스용 SA와는 별개로 관리하는 게 안전하다.
인증 흐름 코드 (Python 예시)
from google.oauth2.service_account import Credentials
from googleapiclient.discovery import build
SCOPES = ["https://www.googleapis.com/auth/androidpublisher"]
creds = Credentials.from_service_account_info(sa_json, scopes=SCOPES)
publisher = build("androidpublisher", "v3", credentials=creds, cache_discovery=False)
result = (
publisher.purchases().subscriptions()
.get(packageName="com.example.app", subscriptionId="sub_1", token=purchase_token)
.execute()
)
creds 생성 시 network call 없음 (인증서 파싱만). .execute() 호출 시점에 token 발급 + API 호출이 일어남.
관련 노트
- Play Console SA는 앱 권한만으로는 purchases.subscriptions.get이 401을 반환한다
- GCP SA key의 사용 흔적은 private_key_id로 필터링한다
- GCP API 키 호출자 IP는 Cloud Audit Data Access Log를 켜야 추적할 수 있다
- GCP API 키의 androidKeyRestrictions 빈 객체는 제한이 없는 것과 같다