"""Google 스프레드시트 쓰기용 refresh token 1회 발급 스크립트. 조직 정책(`iam.disableServiceAccountKeyCreation`)으로 서비스 계정 키를 만들 수 없을 때 사용자 계정 OAuth 로 서버가 시트에 기록하도록 하기 위한 토큰을 받는다. 사전 준비 (Google Cloud Console) 1. API 및 서비스 → 라이브러리 → "Google Sheets API" 사용 2. API 및 서비스 → OAuth 동의 화면 구성(내부 또는 외부) 3. 사용자 인증 정보 → 사용자 인증 정보 만들기 → **OAuth 클라이언트 ID** → 애플리케이션 유형 **데스크톱 앱** → 클라이언트 ID / 보안 비밀 확인 실행 (개발 PC, 브라우저 있는 환경에서) python scripts/google_sheets_authorize.py --client-id --client-secret 또는 환경변수로: GOOGLE_SHEETS_OAUTH_CLIENT_ID / GOOGLE_SHEETS_OAUTH_CLIENT_SECRET 브라우저가 열리면 **시트를 편집할 수 있는 계정**으로 로그인·동의한다. 마지막에 출력되는 refresh token 을 서버 `.env` 의 `GOOGLE_SHEETS_OAUTH_REFRESH_TOKEN` 에 넣는다. 주의 - 출력된 refresh token 은 비밀값이다. Git·메신저에 남기지 말 것. - 토큰은 발급한 사용자 권한으로 동작한다. 그 사용자가 대상 문서를 편집할 수 있어야 하며, 계정 비밀번호 변경·토큰 폐기 시 다시 발급해야 한다. """ from __future__ import annotations import argparse import os import sys SCOPES = ["https://www.googleapis.com/auth/spreadsheets"] def main() -> int: parser = argparse.ArgumentParser(description="Google Sheets refresh token 발급") parser.add_argument("--client-id", default=os.getenv("GOOGLE_SHEETS_OAUTH_CLIENT_ID", "")) parser.add_argument( "--client-secret", default=os.getenv("GOOGLE_SHEETS_OAUTH_CLIENT_SECRET", "") ) parser.add_argument( "--port", type=int, default=0, help="로컬 콜백 포트(기본: 임의 포트)" ) args = parser.parse_args() client_id = (args.client_id or "").strip() client_secret = (args.client_secret or "").strip() if not client_id or not client_secret: print("client id / secret 이 필요합니다. --client-id / --client-secret 또는 환경변수로 넣으세요.") return 2 try: from google_auth_oauthlib.flow import InstalledAppFlow except ImportError: print("google-auth-oauthlib 가 필요합니다: pip install google-auth-oauthlib") return 2 flow = InstalledAppFlow.from_client_config( { "installed": { "client_id": client_id, "client_secret": client_secret, "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "redirect_uris": ["http://localhost"], } }, scopes=SCOPES, ) # prompt=consent 를 줘야 refresh token 이 항상 내려온다. creds = flow.run_local_server(port=args.port, prompt="consent", access_type="offline") if not creds.refresh_token: print("refresh token 을 받지 못했습니다. 계정의 기존 권한을 해제하고 다시 시도하세요.") return 1 print() print("=" * 70) print("서버 .env 에 아래 값을 넣으세요 (비밀값 — 공유 금지)") print("=" * 70) print(f"GOOGLE_SHEETS_OAUTH_CLIENT_ID={client_id}") print("GOOGLE_SHEETS_OAUTH_CLIENT_SECRET=<위에서 쓴 보안 비밀>") print(f"GOOGLE_SHEETS_OAUTH_REFRESH_TOKEN={creds.refresh_token}") print("=" * 70) return 0 if __name__ == "__main__": sys.exit(main())