유튜브 API 업로드가 일주일 뒤 invalid_grant로 실패할 때
유튜브 API 자동 업로드가 일주일쯤 지나 invalid_grant로 멈췄다면 OAuth 동의 화면이 테스트 상태라 refresh token이 7일 만에 만료된 것일 수 있어요. 확인하는 법과 해결 방법 세 가지를 적었어요.
목차 7개
유튜브 API로 영상을 자동으로 올리는 스크립트가 며칠은 잘 되다가, 일주일쯤 지나면 invalid_grant 한 줄만 남기고 실패할 때가 있어요. 코드를 건드리지 않았는데 이렇게 됐다면 Google Cloud 프로젝트의 OAuth 동의 화면이 테스트 상태라서 refresh token이 7일 만에 만료된 것일 수 있어요. 저는 영상을 자동으로 만들어 올리는 프로젝트 두 개에서 똑같이 겪어서, 확인하는 방법과 해결한 방법을 적어 둘게요.
invalid_grant, 이런 증상이면 의심해 보세요
- 업로드가 시작도 못 하고 바로 실패해요. 제 경우 실패한 네 건이 모두 0초 만에 끝났어요.
- 오류 문구는
invalid_grant뿐이에요. - 토큰을 받은 날에서 7일쯤 지난 뒤부터 시작돼요.
업로드 실패: invalid_grant
토큰을 받을 때 응답에 같이 들어 있던 값
"refresh_token_expires_in": 604799
유튜브 API의 OAuth 안내 문서는 access token을 갱신할 때 나오는 invalid_grant를 토큰이 만료됐거나 무효가 됐다는 뜻으로 설명해요. 영상을 보내는 단계가 아니라, 그 전에 access token을 새로 받는 단계에서 막힌 거예요.
원인: 테스트 상태면 refresh token이 7일 뒤 만료돼요
구글 OAuth 2.0 문서에 적혀 있는 규칙이에요. 동의 화면의 사용자 유형이 외부(External)이고 게시 상태가 테스트(Testing)인 프로젝트는 7일 뒤 만료되는 refresh token을 받아요. 요청한 권한 범위가 이름, 이메일 주소, 프로필뿐일 때만 예외예요. 유튜브 업로드 권한은 여기에 들지 않아요.
콘솔 도움말에도 테스트 사용자의 승인은 동의한 때부터 7일 뒤 만료되고, refresh token도 함께 만료된다고 적혀 있어요.
refresh_token_expires_in은 문서에 refresh token의 남은 수명(초)이라고 나와 있어요. 문서는 사용자가 기간을 정해 접근을 허용했을 때만 이 값이 들어간다고 설명하는데, 제가 테스트 상태에서 받은 응답에는 604799가 들어 있었어요. 7일(604,800초)에서 1초 빠진 값이에요. 아래 방법으로 다시 받은 토큰에는 이 필드가 없었어요.
7일과 상관없이 invalid_grant가 났다면 다른 원인일 수 있어요. 같은 문서에는 사용자가 접근을 취소했을 때, 6개월 동안 쓰지 않았을 때, 한 계정이 클라이언트 하나에서 받은 refresh token이 100개를 넘어 가장 오래된 것이 무효가 됐을 때도 적혀 있어요.
테스트 상태인지 확인하는 방법
- Google Cloud 콘솔에서 프로젝트를 고르고 Google Auth Platform의 Audience 화면을 열어요. 도움말에 따르면 사용자 유형과 게시 상태(Testing 또는 In production)를 여기서 관리해요.
- 토큰을 받았을 때의 응답이나 저장해 둔 토큰 파일에
refresh_token_expires_in이 있는지 봐요. - 토큰을 받은 날짜와 실패가 시작된 날짜를 비교해요. 저는 8월 19일 10시 21분에 받은 토큰이 8월 26일 10시 21분에 만료됐어요.
해결 방법 세 가지
앱을 게시해서 프로덕션으로 바꾸기
앱 게시 버튼을 누르면 게시 상태가 프로덕션(In production)이 되고, 테스트 상태에만 붙는 7일 규칙을 받지 않아요. 누르기 전에 알아 둘 것이 있어요.
- 민감하거나 제한된 범위를 검증 전에 요청하면 동의 화면에 확인되지 않은 앱 경고가 떠요. 이런 앱에 동의할 수 있는 사용자는 프로젝트 전체 기간에 100명까지예요.
- 검증이 필요 없는 경우에 개인용 앱(사용자 100명 미만)이 들어 있어요. 경고 화면을 넘기고 계속 쓸 수 있다고 적혀 있어요.
- 검증을 받으려면 요구 사항을 채워야 해요. 소유를 확인한 도메인의 홈페이지, 개인정보처리방침, 동의 과정을 담은 시연 영상, 그 범위가 필요한 이유 설명이에요.
- 따로 있는 제한도 있어요. videos.insert 문서에는 2020년 7월 28일 이후 만든 미검증 API 프로젝트로 올린 영상은 비공개로 제한되고, 감사를 받아야 풀린다고 적혀 있어요.
유튜브 업로드 범위가 민감한 범위로 분류되는지는 제가 읽은 문서에 적혀 있지 않았어요. 콘솔에서 직접 확인해 보세요. 테스트 상태에서 이미 받은 토큰이 게시 뒤에 어떻게 되는지도 문서에서 찾지 못했어요.
저는 처음에 홈페이지와 개인정보처리방침 주소를 비워 두라는 안내를 따랐는데, 프로덕션으로 바꾸는 데는 그 주소가 필요해서 틀린 안내였어요. 제 프로젝트에서는 앱 게시가 되지 않아서 다음 방법으로 갔어요.
7일 만료가 없는 다른 클라이언트로 다시 동의하기
제가 쓴 방법이에요. 제 다른 프로젝트에 7월에 받은 토큰이 두 달 넘게 살아 있는 OAuth 클라이언트가 있었어요. 그 클라이언트로, 영상을 올릴 채널 계정이 새로 동의해서 refresh token을 다시 받았어요. 클라이언트는 앱의 신분증이고 refresh token은 어느 채널에 올릴지를 정하는 값이라, 영상은 동의한 계정의 채널로 올라가요.
한 번 막힌 곳이 있어요. 그 클라이언트는 웹 애플리케이션 유형이라 localhost 콜백 주소가 등록돼 있지 않았고, 로컬 인증 스크립트는 400 오류를 냈어요. 그래서 OAuth Playground에서 토큰을 받았어요. 구글 안내에 따르면 클라이언트의 승인된 리디렉션 URI에 https://developers.google.com/oauthplayground를 넣고, Playground 설정에서 Use your own OAuth credentials를 체크해 내 클라이언트 정보를 적어요. 토큰을 받은 뒤에는 그 URI를 지우라고 해요.
다시 동의받으면서 권한 범위는 youtube와 youtube.upload 두 개로 줄였어요.
아쉬운 점은 할당량이에요. 유튜브 API 할당량은 프로젝트마다 주어져요. 지금은 클라이언트 하나를 프로젝트 네 개가 같이 써서 하루 업로드 한도를 나눠 써요.
테스트 상태로 두고 일주일마다 다시 동의하기
7일마다 새로 로그인하는 방법이에요. 스크립트로 대신할 수는 없었어요. access token을 갱신해도 refresh token은 새로 나오지 않고(문서의 갱신 응답 예시에도 없어요), 다시 동의하는 건 사람이 브라우저에서 직접 눌러야 해요. 자동 업로드라면 매주 한 번씩 손이 가요.
제가 겪은 일
- 과학 쇼츠를 자동으로 만드는 프로젝트: 8월 19일에 받은 토큰이 26일에 만료됐고, 27일에 업로드가
invalid_grant로 실패했어요. 그날 다른 클라이언트로 다시 동의해서 해결했어요. - 어린이 동화 영상을 자동으로 만드는 프로젝트: 9월 7일에 토큰을 받았고 12일까지는 올라갔어요. 14일부터 네 건이 0초 만에 실패했고, 원인은 10월 1일에 찾았어요. 연결하던 날 동의 화면이 테스트 상태였고 날짜도 7일 규칙과 맞아요. 다만 실패한 뒤에 콘솔을 다시 열어 확인한 건 아니에요. 여기도 같은 클라이언트로 바꿨어요.
코드에서 같이 고친 두 가지
토큰 파일이 있다고 로그인된 건 아니에요. 제 인증 스크립트는 token.json이 있기만 하면 재인증을 건너뛰었어요. 실제로 갱신을 한 번 해 보고, 실패하면 새 로그인으로 넘어가게 고쳤어요. 인증 직후에 연결된 채널이 맞는지 확인하는 단계도 넣었어요. 확인은 이런 식이에요.
// 파일이 있는지가 아니라, 실제로 갱신되는지 본다
async function isTokenAlive(t) {
const res = await fetch("https://oauth2.googleapis.com/token", {
method: "POST",
body: new URLSearchParams({
client_id: t.client_id,
client_secret: t.client_secret,
refresh_token: t.refresh_token,
grant_type: "refresh_token",
}),
});
return res.ok; // 만료됐으면 invalid_grant가 돌아온다
}
실패한 이유를 화면에 보여 주세요. 두 번째 프로젝트에서는 업로드 버튼을 눌러도 아무 일도 없는 것처럼 보였어요. 서버가 설정에 토큰이 적혀 있는지만 확인했고, 실패한 이유를 화면에 표시하지 않았기 때문이에요. 오류 문구를 영상 카드에 보여 주게 고쳤어요. 구글 문서도 받아 둔 refresh token이 더는 동작하지 않을 수 있다고 보고 코드를 쓰라고 해요.
한눈에 보기
| 방법 | 좋은 점 | 아쉬운 점 |
|---|---|---|
| 앱 게시(프로덕션) | 테스트 상태의 7일 규칙을 받지 않아요 | 검증 전에는 경고 화면과 사용자 100명 한도가 있고, 검증에는 홈페이지·개인정보처리방침·시연 영상이 필요해요 |
| 만료 없는 다른 클라이언트로 재동의 | 오래 써 온 클라이언트라 그날 해결됐어요 | 할당량을 같이 써요. 웹 유형이면 OAuth Playground를 거쳐야 했어요 |
| 테스트 유지, 매주 재동의 | 바꿀 것이 없어요 | 7일마다 사람이 브라우저에서 동의해야 해요 |
댓글 0
아직 댓글이 없어요. 첫 이야기를 남겨 주세요.