Troubleshooting 2026.08.11 6 min read

Google OAuth 테스트 모드의 7일 refresh token 함정

이슈 발생: 2026-08-09 · 발행 기준: 2026-08-10 11:52 KST
환경: Google OAuth, rclone, Kubernetes 정기 백업, Cloudflare Access

rclone용 Google OAuth client를 직접 만들고 인증도 성공했는데 한 가지 운영 문제가 남았다. OAuth 동의 화면이 External / Testing 상태면 특정 scope를 승인해 발급받은 refresh token이 7일 뒤 만료될 수 있다. 매일 자동으로 돌아야 하는 백업에는 치명적이다.

이번에 중요한 질문은 두 가지였다.

  1. OAuth 앱을 Production으로 바꾸면 7일 제한이 없어지는가?
  2. 같은 Google Cloud 프로젝트에서 사용 중인 다른 OAuth client의 ID나 secret도 바뀌는가?

결론은 동의 화면의 게시 상태를 Production으로 전환해도 기존 OAuth client의 ID, secret, 승인된 JavaScript origin과 redirect URI가 자동으로 바뀌지는 않는다. 다만 Testing 상태에서 이미 받은 token이 자동으로 장기 token으로 변한다고 가정하면 안 된다. 게시 후 rclone을 다시 승인해 새 refresh token을 발급하는 편이 안전하다.

OAuth에서 구분해야 할 세 층

Google Cloud 프로젝트
  ├─ OAuth 동의 화면: Internal/External, Testing/Production
  ├─ OAuth client A: rclone Desktop client
  └─ OAuth client B: Cloudflare Access용 Web client

동의 화면의 게시 상태는 프로젝트 차원의 설정이다. 반면 client ID, client secret, origin, redirect URI는 개별 OAuth client의 설정이다.

따라서 Production 전환 전에 같은 프로젝트 안의 다른 client가 무엇에 쓰이는지 확인해야 하지만, 전환 자체가 그 client의 자격 증명을 회전시키는 작업은 아니다.

Testing 상태에서 왜 7일인가

Google OAuth 문서에 따르면 외부 사용자 유형의 앱이 Testing 상태이고 특정 scope를 요청하면 refresh token이 7일 뒤 만료될 수 있다. 브라우저 로그인 직후에는 모든 것이 정상이라 이 문제를 놓치기 쉽다.

자동 백업에서 access token은 짧게 사용되고, 만료되면 refresh token으로 새 access token을 받는다. refresh token 자체가 사라지면 사람의 브라우저 재인증 없이는 다음 백업이 계속 실패한다.

access token 만료
  → refresh token으로 재발급
  → refresh token도 만료됨
  → interaction_required / invalid_grant
  → 자동 백업 중단

Production 전환 전 점검

같은 프로젝트에서 Cloudflare Access용 Google 로그인을 사용하고 있었기 때문에 다음 항목을 먼저 기록했다.

  • OAuth client 목록과 각 client 유형(Desktop/Web)
  • Web client의 승인된 JavaScript origin
  • Web client의 승인된 redirect URI
  • Cloudflare Access의 Google IdP 설정
  • rclone이 사용할 Desktop client
  • OAuth 동의 화면의 scope와 test user

비밀값은 출력하지 않고 이름, 유형, URI 존재 여부만 비교한다. 화면 캡처를 남길 때도 client secret과 token은 가린다.

안전하게 전환한 순서

  1. rclone 전용 Desktop OAuth client를 별도로 만든다.
  2. Google Drive API가 활성화되어 있는지 확인한다.
  3. OAuth 동의 화면을 External / Production으로 게시한다.
  4. 기존 Web client의 ID, redirect URI가 그대로인지 확인한다.
  5. Cloudflare Access 로그인을 실제로 시험한다.
  6. rclone을 다시 승인해 새 refresh token을 발급한다.
  7. Kubernetes Secret을 새 설정으로 교체한다.
  8. 수동 백업과 정기 백업을 각각 검증한다.

여기서 5번과 6번의 순서가 중요하다. 프로젝트 공용 설정을 바꾼 직후에는 기존 서비스가 정상인지 먼저 확인하고, 그다음 백업용 token을 갱신하면 문제 범위를 분리할 수 있다.

Kubernetes 반영 시 주의점

rclone 설정 파일에는 client secret과 token JSON이 들어간다. 임시 파일의 권한과 명령 기록을 점검해야 한다.

# Secret 값을 보지 않고 메타데이터만 확인
kubectl -n backup get secret <rclone-secret> \
  -o custom-columns=NAME:.metadata.name,UPDATED:.metadata.creationTimestamp

# 교체 후 새 Job으로 검증
kubectl -n backup create job \
  --from=cronjob/<backup-cronjob> <oauth-validation-job>

Secret을 갱신했다고 이미 실행 중인 Pod가 항상 즉시 새 값을 읽는 것은 아니다. 환경변수로 주입했다면 새 Pod가 필요하고, volume이면 갱신 방식과 애플리케이션의 재읽기 여부를 확인한다. 일회성 Job이라면 새 Job 생성이 가장 명확하다.

검증 결과를 판단하는 기준

  • Cloudflare Access의 기존 Google 로그인이 그대로 성공한다.
  • 기존 Web client의 ID/secret/redirect URI가 변경되지 않았다.
  • rclone이 새 token으로 Drive 목록 조회 및 업로드에 성공한다.
  • 백업 Job 로그에 invalid_grant, interaction_required가 없다.
  • 다음 정기 실행에서도 브라우저 개입 없이 성공한다.
  • 복원 시험까지 통과한다.

오해하기 쉬운 부분

Production이면 Google 검증이 항상 필요한가?

앱이 요청하는 scope와 사용자 범위에 따라 검증 요구가 달라진다. Production 전환과 Google의 앱 검증 완료는 같은 말이 아니다. 콘솔에 표시되는 경고와 사용자 제한을 실제 scope 기준으로 확인해야 한다.

게시하면 기존 key가 바뀌는가?

아니다. 게시 상태 변경은 자동 key rotation이 아니다. client secret을 직접 재설정하거나 client를 삭제하지 않는 한 별개의 작업이다.

Testing 때 받은 token을 계속 써도 되는가?

운영 자동화라면 권하지 않는다. 게시 후 새로 승인하고 새 token으로 실제 Job을 검증하는 것이 명확하다.

재발 방지

  • OAuth client마다 용도를 이름에 표시한다.
  • 다른 서비스가 함께 쓰는 프로젝트라면 영향도 표를 먼저 만든다.
  • invalid_grant와 연속 백업 실패에 알림을 둔다.
  • token 교체 절차와 복구 절차를 함께 문서화한다.
  • 비밀값을 화면·로그·Git에 남기지 않는다.

참고 자료