헤르메스 Google 인증이 7일마다 풀린 이유, Testing 설정이 문제였다

디스코드에서 구글 캘린더에 등록 실패
디스코드에서 구글 캘린더에 등록 실패

유튜브를 보면서 헤르메스에 Google Calendar, Gmail, Drive 같은 서비스를 연결했습니다.

처음에는 잘 됐습니다. Google Cloud에서 OAuth를 만들고 External + Testing으로 설정한 뒤 제 Google 계정을 테스트 사용자로 추가했습니다. 제가 본 영상들도 대부분 비슷한 방식이었습니다.

그런데 며칠 동안 잘 돌아가던 헤르메스가 어느 날 Google Calendar 작업을 처리하지 못했습니다. 원인은 앱 게시 상태가 Testing 이었기 때문이었습니다.
In production으로 전환하는게 귀찮아서 그냥 재인증하면 되겠지했는데 재인증이 3번째 반복되다보니 오히려 이쪽이 더 귀찮아졌습니다.

검색을 해도 저와 비슷한 증상을 겪었다는 후기를 찾지 못했습니다.
'나만 그런 것인가? 처음에 설정을 잘못한 건가?’
결국 Google 공식 문서를 하나씩 확인하면서 직접 해결해보기로 했습니다. 제 환경에서는 아래 방법으로 7일마다 반복되던 재인증 문제가 해결됐습니다.

이 방법을 사용할 수 있는 환경

본격적으로 방법을 설명하기 전에 이 부분부터 먼저 확인하는 게 좋습니다.

이 글의 방법으로 In production 전환을 진행하려면 Google에 본인이 소유한 도메인을 확인시킬 수 있어야 합니다.

대략 이렇게 보면 됩니다.

도메인 유형이 방법 적용 가능 여부비고
본인 소유 도메인 + WordPress 또는 웹호스팅가능직접 DNS 제어 가능
본인 소유 도메인을 연결한 티스토리가능커스텀 도메인 연결 시 DNS 설정 가능
기본 아이디.tistory.com어려움본인 소유 도메인으로 검증할 수 없음
기본 blog.naver.com/…어려움본인 소유 도메인으로 검증할 수 없음

왜냐하면 구글은 Production OAuth 앱에 사용하는 홈페이지와 개인정보처리방침 등의 “도메인을 개발자가 소유하고 검증할 수 있어야 한다"고 요구하기 때문입니다.

네이버 블로그의 naver.com이나 기본 티스토리의 tistory.com은 블로그 운영자가 소유한 도메인이 아닙니다. 네이버는 현재 블로그에 개인 도메인을 연결하는 방식도 지원하지 않습니다.

티스토리는 다릅니다. 본인이 소유한 개인 도메인을 연결할 수 있으므로, oauth.내도메인.com같은 주소를 티스토리에 연결했다면 사용할 수 있습니다. 아마도(?)

저는 이미 개인 도메인과 웹호스팅이 있기 때문에 별도의 oauth.example.com 형태의 서브도메인을 만들어 진행했습니다.

기본 네이버 블로그나 아이디.tistory.com만 사용 중이라면 먼저 도메인 문제를 해결해야 합니다.

7일마다 인증이 풀리는 이유

Google OAuth 테스트 사용자 7일 만료 설명
Google OAuth 테스트 사용자 7일 만료 설명

제가 처음 설정한 상태는 External + Testing이었습니다.

구글 공식 문서를 확인해 보니 Testing 상태에서는 테스트 사용자가 승인한 OAuth 권한이 동의 시점으로부터 7일 뒤 만료됐다고 쓰여있었습니다.

Authorizations by a test user will expire seven days from the time of consent. If your OAuth client requests an offline access type and receives a refresh token, that token will also expire.

https://support.google.com/cloud/answer/15549945#zippy=%2Ctesting

여기서 조금 헷갈릴 수 있는데 요약하자면, 제가 사용한 Gmail, Drive, Calendar 같은 권한에서는 offline 접근으로 받은 refresh token도 7일 뒤 함께 만료되었습니다.

해결 방향은 Testing에서 In production으로 변경

Google 인증 플랫폼 - 대상
Google 인증 플랫폼 – 대상

Google 인증 플랫폼에는 앱 게시 상태가 Testing(테스트 중)In production(프로덕션 단계)으로 나뉘어 있습니다.

Testing은 말 그대로 개발과 테스트를 위한 상태입니다. 제가 해결하려던 것은 바로 이 Testing 상태에 적용되던 7일 제한이었습니다. In production으로 전환하면 이 7일 테스트 제한에서 벗어날 수 있습니다.

다만 여기서 하나는 구분해야 합니다. In production으로 바꿨다고 refresh token이 절대 영구적으로 유지된다는 뜻은 아닙니다. 사용자가 권한을 철회하거나, 장기간 사용하지 않거나, Google의 다른 토큰 무효화 조건이 발생하면 다시 인증해야 할 수도 있습니다.

제가 없앤 것은 “Testing 상태라서 강제로 반복되던 7일 제한” 입니다.

앱 게시 버튼의 비활성화 문제로 서브도메인 생성 고민

처음에는 단순히 Testing → In production만 바꾸면 되는 줄 알았습니다. 그런데 앱 게시버튼이 비활성화상태였습니다. 이걸 활성화하기 위해서는 브랜딩 페이지에서 앱 홈페이지와 개인정보처리방침 등의 URL을 등록해야 했고, 그 URL이 있는 도메인이 제 소유라는 것을 확인할 필요가 있었습니다.

여기서 저는 조금 고민했습니다.

사실 이미 운영 중인 WordPress 블로그가 있으니 페이지 몇 개를 만드는 것 자체는 어렵지 않았습니다. 그런데 OAuth 인증을 위해 만든 앱 소개와 개인정보처리방침 페이지가 기존 블로그 방문자에게 보이는 건 별로 탐탁지 않았습니다.

그래서 기존 블로그와 분리된 서브도메인을 하나 만들기로 했습니다.

서브도메인에 필요한 파일 3개

처음에는 서브도메인에 WordPress를 하나 더 설치해야 하나 생각했습니다. 그럴 필요가 없었습니다.

제가 최종적으로 만든 구조는 아주 단순합니다.

oauth.example.com/
├── index.html
├── privacy.html
└── terms.html

index.html은 앱 소개 페이지, privacy.html은 개인정보처리방침, terms.html은 서비스 약관입니다. Google 입장에서 중요한 것은 이 사이트가 WordPress인지 PHP인지 정적 HTML인지가 아닙니다. 각 페이지가 공개적으로 접근 가능하고 앱의 기능을 설명하며 개인정보처리방침으로 연결되어 있으면 됩니다.

개인정보처리방침에는 Google 사용자 데이터에 어떻게 접근하고 사용하고 저장하고 공유하는지도 실제 동작에 맞게 설명해야 합니다.

서비스 약관은 필수는 아니지만 저는 어차피 HTML 파일 하나 더 만드는 정도라 같이 만들었습니다.

검색엔진에 노출할 목적이 없어서 각 파일에는 아래 메타 태그도 넣었습니다.

<meta name="robots" content="noindex, nofollow">

로그인 기능도 없고, PHP도 없고, 데이터베이스도 없고, JavaScript도 없는 정적 HTML 세 장뿐입니다.

privacy.html, terms.html처럼 URL 뒤에 .html이 붙는 것을 선호하지는 않지만 OAuth 인증에는 아무 의미가 없는 차이라서 그대로 두었습니다.

Search Console에 이미 등록된 도메인이라면 먼저 속성 유형부터 확인

서치 콘솔의 도메인 속성과 URL 접두어 속성
서치 콘솔의 도메인 속성과 URL 접두어 속성

제 블로그는 이미 Google Search Console에 등록되어 있었습니다. 그래서 처음에는 도메인 소유권 확인을 다시 할 필요가 없다고 생각했습니다.

그런데 구글 공식 문서에는 “URL 접두사"나 “사이트" 속성이 아닌, 도메인 속성 (DNS 레벨) 을 확인해야 한다고 나와 있습니다.

You must verify the Domain Property (DNS-level), rather than a “URL prefix" or “Site,” property.

https://support.google.com/cloud/answer/13804266#zippy=%2Ca-project-owner-has-not-yet-verified-one-or-more-of-your-authorized-domains

처음에 URL 접두사 속성으로 등록했다면 도메인 속성으로 한 번 더 등록해야 합니다. 기존의 속성을 삭제할 필요는 없습니다.

구글 서치 콘솔의 속성 리스트
구글 서치 콘솔의 속성 리스트

자신의 사이트의 속성이 도메인인지 URL 접두사인지는 속성 목록 앞에 https://가 붙어 있는지를 보면 알 수 있습니다.

https://example.com/

이것은 URL 접두사 속성입니다.

Search Console의 도메인 속성은 다음처럼 프로토콜 없이 표시됩니다.

example.com

둘은 범위가 다릅니다.

Google 공식 설명에 따르면 URL 접두사 속성은 지정한 프로토콜과 호스트 범위만 포함합니다.

반면 도메인 속성은 해당 도메인의 모든 하위 도메인과 http/https를 함께 포함합니다.

요약하자면, 속성 목록에서 저처럼 도메인 앞에 https://가 붙어 있다면 기존 속성은 URL 접두사 속성입니다. 이 속성만으로는 새로 만든 oauth.example.com까지 포함되지 않습니다.
그래서 기존 URL 접두사 속성은 그대로 두고, example.com도메인 속성으로 하나 더 추가합니다. 이 경우, oauth.example.com을 별도의 Search Console 속성으로 또 추가할 필요는 없습니다. 루트 도메인을 도메인 속성으로 확인하면 하위 도메인까지 포함됩니다.

DNS TXT로 도메인 소유권 확인

DNS 레코드를 통해 도메인 소유권 확인
DNS 레코드를 통해 도메인 소유권 확인

Search Console에서 도메인 속성을 추가하면 DNS를 이용한 소유권 확인 절차가 나옵니다.

Google이 다음처럼 생긴 TXT 값을 보여줍니다.

google-site-verification=랜덤문자열
TXT 레코드 추가
TXT 레코드 추가

이 값을 도메인을 관리하는 업체의 DNS 설정에 새 TXT 레코드로 추가했습니다. 기존의 TXT 레코드를 수정하면 안 됩니다. 추가해야 합니다.

TXT 레코드 반영에는 시간이 필요

서치 콘솔 소유권 확인 실패
서치 콘솔 소유권 확인 실패

TXT 레코드를 추가한 뒤 바로 Search Console에서 확인 버튼을 눌러도 실패로 뜨는 경우가 있습니다. 이건 설정이 잘못된 것이 아니라 DNS 변경이 인터넷에 바로 반영되지 않기 때문입니다. 수정한 게 아닌, 저처럼 새로 추가한 경우는 보통 몇 분 이내에 반영됩니다. 실제로 저는 3분도 채 걸리지 않았습니다.

Google도 DNS 레코드가 보이지 않으면 1~2일 뒤 다시 시도하라고 안내합니다.

그리고 한 번 발급받은 “google-site-verification=랜덤문자열" 값은 바뀌지 않으니 안심하고 창을 닫고 기다리면 됩니다.

Windows CLI에서 DNS 반영 여부를 직접 확인

Power Shell에서 DNS TXT 레코드 조회
Power Shell에서 DNS TXT 레코드 조회

굳이 이렇게까지 할 필요는 없지만 Windows의 CMD나 파워쉘에서도 DNS TXT 레코드가 반영이 되었는지 조회 가능합니다.

CMD(명령 프롬프트)에서는 다음 명령을 사용합니다.

nslookup -type=TXT example.com 8.8.8.8

뜻은 간단합니다.

  • nslookup: DNS 조회
  • -type=TXT: TXT 레코드만 조회
  • example.com: 확인할 도메인
  • 8.8.8.8: Google Public DNS에 직접 질의

PowerShell에서는 다음 명령으로 사용할 수 있습니다.

Resolve-DnsName example.com -Type TXT -Server 8.8.8.8

결과에 “google-site-verification=…"이 보이기 시작하면 외부 DNS에서도 TXT 레코드를 확인할 수 있다는 뜻입니다.

Google Auth Platform의 브랜딩 설정

Google 인증 플랫폼 - 브랜딩
Google 인증 플랫폼 – 브랜딩

도메인 소유권 확인까지 끝난 뒤 Google Auth Platform의 브랜딩 화면으로 돌아갔습니다.

제가 사용한 구조를 예로 들면 다음과 같습니다.

애플리케이션 홈페이지
https://oauth.example.com/
개인정보처리방침
https://oauth.example.com/privacy.html
서비스 약관
https://oauth.example.com/terms.html
승인된 도메인
example.com

승인된 도메인에는 oauth.example.com 전체 주소가 아니라 제가 Search Console에서 소유권을 확인한 루트 도메인 example.com을 넣었습니다. https://는 넣지 않습니다.

반면 홈페이지와 개인정보처리방침에는 사용자가 실제로 접속할 수 있는 전체 HTTPS URL을 입력해야 합니다.

Google은 OAuth에 사용하는 홈페이지와 개인정보처리방침 등의 도메인을 개발자가 소유하고 Search Console을 통해 확인할 수 있어야 한다고 설명합니다.

앱 게시 버튼이 활성화 및 프로덕션 전환 완료

브랜딩과 승인된 도메인 설정을 끝내고 대상 화면으로 돌아갔더니 그동안 비활성화돼 있던 앱 게시 버튼이 활성화됐습니다.

여기서 앱 게시는 Google에 정식 OAuth 검증 심사를 신청한다는 뜻과는 다릅니다. 제 경우에는 혼자 사용하는 개인용이라 별도의 OAuth 검증을 진행하지 않았고, 게시 상태만 프로덕션 단계(In production)로 변경했습니다.

버튼을 누르고 앱 게시 과정에서는 오류가 나왔습니다.

Error: 앱을 업데이트하는 중에 오류가 발생했습니다.

처음에는 뭔가 잘못된 줄 알았는데 화면에 나온 재시도를 눌렀더니 바로 정상 처리됐습니다. 물론 이 오류가 항상 재시도로 해결된다는 뜻은 아닙니다. 제가 이번 작업에서 실제로 겪은 결과가 그랬다는 정도로만 참고하면 됩니다.

Google 인증 플랫폼 프로덕션 설정 완료
Google 인증 플랫폼 프로덕션 설정 완료

최종적으로 게시 상태가 프로덕션 단계로 바뀐 것을 확인했습니다.

Production으로 바꾼 뒤 헤르메스에서 다시 인증

이제 한 단계만 남았습니다. Production으로 전환한 뒤 실제로 Google API를 사용하는 환경에서 OAuth 인증을 다시 진행했습니다. 저는 헤르메스를 사용하고 있기 때문에 헤르메스에게 재인증을 요청했습니다.

기존 OAuth 인증은 Testing 상태에서 발급받은 것이었기 때문에 헤르메스에 Google OAuth를 다시 인증하도록 요청했습니다.

새로 OAuth 인증을 진행한 뒤에는 새 활성 토큰에 refresh token이 존재하는 것을 확인했고, 기존과 동일한 권한 범위를 유지했다고 보고했습니다.

그리고 실제로 다음 서비스에 접근 가능한지도 확인했습니다.

  • Gmail: 성공
  • Google Drive: 성공
  • Google Calendar: 성공

여기서 중요한 것은 헤르메스 내부에서 토큰을 어떤 코드로 처리했는지가 아닙니다.

Production 전환 후 실제 사용하는 자동화 환경에서 다시 OAuth 인증을 진행하고, 필요한 Google 서비스에 정상적으로 접근할 수 있는지 직접 확인했다는 점입니다.

Google Auth Platform의 OAuth 게시 상태를 Testing에서 In production으로 변경했어.

기존 Google OAuth 인증은 Testing 상태에서 발급받은 것이므로, 현재 In production 상태를 기준으로 Google 계정을 다시 인증하고 새 OAuth 인증 정보를 받도록 처리해줘.

다음 순서로 진행해줘.

  1. 먼저 현재 Google OAuth 인증 방식과 인증 정보(refresh token 등)가 어디에 저장되어 있는지 확인해줘.
  2. 기존 인증 정보를 바로 삭제하지 말고, 필요하면 복구할 수 있도록 현재 상태를 확인하거나 백업해줘.
  3. 현재 사용 중인 OAuth Client ID/Client Secret과 기존 Google API scope는 변경하지 마.
  4. 기존 Testing 상태에서 받은 refresh token을 그대로 재사용하지 말고, Google OAuth 인증 절차를 다시 진행해서 In production 상태에서 새 인증을 받아줘.
  5. 브라우저에서 Google 로그인이나 권한 승인 등 내가 직접 해야 하는 단계가 나오면 URL과 해야 할 일을 알려주고 그 시점에서 기다려줘.
  6. 새 refresh token이 정상적으로 발급되었는지 확인하고, 기존 인증 정보 대신 새 인증 정보가 사용되도록 저장해줘.
  7. 재인증 완료 후 현재 연결되어 있는 Google 서비스(Gmail, Google Drive, Google Calendar 등)에 실제로 접근 가능한지 각각 간단하게 테스트해줘.
  8. 테스트 결과와 함께 아래 내용을 최종 보고해줘.
    • OAuth 재인증 성공 여부
    • 새 refresh token 발급 여부
    • 인증 정보 저장 위치
    • 테스트한 Google 서비스와 결과
    • 기존 인증 정보가 어떻게 처리되었는지

주의:

  • Google Cloud 프로젝트나 OAuth Client를 새로 만들지 마.
  • API scope를 임의로 추가하거나 삭제하지 마.
  • 기존 인증 정보를 확인하기 전에 무작정 파일이나 토큰을 삭제하지 마.
  • 인증 과정에서 새 refresh token이 발급되지 않는다면 그 이유를 먼저 확인하고, 필요한 경우에만 OAuth의 재동의(consent) 절차를 사용해 새 refresh token을 발급받도록 해줘.

검수 – 낮에 실패했던 Calendar 작업을 다시 실행

디스코드에서 정상처리 확인
디스코드에서 정상처리 확인

가장 확실한 확인은 실제로 실패했던 작업을 다시 해보는 것이었습니다.

낮에 OAuth 문제 때문에 처리하지 못했던 Google Calendar 작업을 디스코드에서 헤르메스에게 다시 요청했습니다.

이번에는 정상적으로 처리됐습니다.

실제로 진행한 순서

전체 과정을 다시 정리하면 다음과 같습니다.

  1. Google Auth Platform이 External + Testing인지 확인
  2. 본인 소유 도메인을 사용할 수 있는지 확인
  3. OAuth용 서브도메인 생성
  4. Search Console에 루트 도메인을 도메인 속성으로 추가
  5. DNS에 google-site-verification TXT 추가
  6. DNS 반영 후 Search Console 소유권 확인
  7. 서브도메인에 index.html, privacy.html, terms.html 업로드
  8. Google Auth Platform 브랜딩에서 앱 도메인 입력
  9. 승인된 도메인에 루트 도메인 추가
  10. 대상에서 앱 게시
  11. 게시 상태가 Production인지 확인
  12. 헤르메스 Google OAuth 재인증
  13. Gmail, Drive, Calendar 접근 확인
  14. 이전에 실패했던 Calendar 작업 재실행

DNS 반영 때문에 중간에 기다리는 시간이 생길 수 있다는 점을 빼면 실제 작업 자체는 생각보다 복잡하지 않았습니다.

주의할 점

첫째, In production으로 바꿨다고 refresh token이 영원하다는 의미는 아닙니다. 이번 변경의 목적은 Testing 상태에 강제로 적용되던 7일 제한을 없애는 것이었습니다. 사용자가 앱 권한을 철회하거나 다른 토큰 무효화 조건이 발생하면 다시 인증해야 할 수 있습니다.

둘째, Google OAuth에 등록한 홈페이지와 개인정보처리방침은 인증이 끝났다고했더라도 삭제하지 않는 편이 좋습니다. 구글은 Production 앱의 홈페이지를 계속 공개 접근 가능한 상태로 유지하도록 요구하고, 개인정보처리방침 역시 실제 데이터 처리 방식과 맞게 유지하도록 요구합니다.

셋째, Search Console 소유권 확인용 TXT 레코드도 확인이 끝났다고 바로 삭제하지 않는 편이 좋습니다. 구글은 소유권 확인 상태를 유지하려면 확인에 사용한 레코드를 유지하라고 안내합니다.

정리

유튜브를 따라 Google OAuth를 처음 연결했을 때는 External + Testing이라는 설정이 별문제 없어 보였습니다. 실제로 처음 며칠 동안은 아무 문제 없이 돌아갔습니다. 하지만 개인 자동화를 계속 운영할 생각이라면 7일마다 다시 인증하는 건 상당히 귀찮습니다. 한 번, 두 번 재인증을 하고 나서야 이 설정을 제대로 들여다보게 됐습니다.

제 경우에는 별도의 OAuth 전용 서브도메인을 만들고 정적 HTML 3개를 올린 뒤, Search Console에서 도메인 소유권을 확인하고 Google Auth Platform을 In production으로 전환했습니다. 그 후 헤르메스를 다시 인증했고 Gmail, Drive, Calendar 접근과 실제 Calendar 작업까지 정상 동작하는 것을 확인했습니다.

저처럼 개인 자동화를 만들면서 유튜브의 OAuth 설정을 그대로 따라 한 뒤 “왜 일주일마다 인증이 풀리지?” 하고 있었다면, 우선 Google Auth Platform의 게시 상태가 Testing인지부터 확인해 보는 것이 좋습니다.