📌 매일 정해진 시간에 GitHub Actions가 Google Search Console 실적을 가져와 Teams 채널에 카드로 자동 게시합니다. 이 글은 그 파이프라인을 만들며 부딪힌 webhook·인증 문제와 해결 과정을 정리합니다.
매일 정해진 시간에 GitHub Actions가 실행됩니다. 실행된 스크립트는 Google Search Console(GSC)에서 검색 성과를 가져와 Teams의 GSC 리포트 채널에 카드 형태로 올립니다.
GitHub Actions
→ GSC API 호출
→ 노출·클릭·CTR·평균순위 계산
→ Teams용 카드 JSON 생성
→ Power Automate webhook URL에 POST
→ Teams 채널에 카드 게시현재 구현 파일은 다음과 같습니다.
scripts/gsc-report.mjs — GSC 조회와 카드 생성·전송.github/workflows/gsc-report.yml — 매일 실행하는 GitHub Actions 워크플로우Webhook은 **"어떤 이벤트가 발생했을 때, 정해진 URL로 HTTP 요청을 보내는 방식"**입니다.
일반적인 API 호출은 클라이언트가 "새 소식 있나요?"라고 서버에 물어보는 구조입니다.
클라이언트 → 서버
"새 데이터 있나요?"Webhook은 반대입니다. 이벤트가 발생한 시스템이 미리 등록된 URL로 먼저 알려줍니다.
이벤트 발생 시스템 → webhook URL
"새 데이터가 생겼습니다."이번 자동화에서는 GitHub Actions의 Node 스크립트가 발신자입니다. 매일 GSC 데이터를 계산한 뒤 Teams 알림을 위한 webhook URL에 POST 요청을 보냅니다.
Node 스크립트 → Power Automate webhook URLTeams 입장에서 외부 시스템이 Teams 안으로 메시지를 넣는 것이므로 Incoming Webhook이라고 합니다.
반대로 Teams 사용자가 메시지를 보내면 외부 시스템이 반응하는 형태는 Outgoing Webhook이라고 부르지만, 이번 자동화와는 관계없습니다.
기존 방식은 Teams 채널에 Incoming Webhook Connector를 추가하고 URL 하나를 받는 방식입니다.
Node 스크립트
→ Teams Incoming Webhook URL
→ Teams 채널장점은 단순합니다.
⚠️ 하지만 Microsoft 365/O365 Connector 기반 Incoming Webhook은 앞으로 신규 구현의 기반으로 쓰기 적합하지 않습니다. Microsoft는 Teams 알림 자동화의 대체 경로로 Workflows/Power Automate 사용을 안내합니다. Microsoft Teams Incoming Webhook 문서
Power Automate는 Microsoft의 업무 자동화 서비스입니다. 여러 시스템의 이벤트를 받아 조건 처리, 데이터 변환, 메시지 전송 같은 작업을 연결할 수 있습니다.
이번 구성에서 Power Automate는 "중간 전달자" 역할을 합니다.
GSC 리포트 스크립트
→ Power Automate Flow
→ Teams GSC 리포트 채널이번 Flow는 두 단계로 이루어집니다.
즉, Power Automate는 "외부 데이터 수신 → Teams 카드 게시"를 담당합니다.
| 항목 | 기존 Incoming Webhook | Power Automate Workflow |
|---|---|---|
| Microsoft 구성 요소 | O365/M365 Connector | Power Automate + Teams |
| 구조 | 외부 시스템이 Teams로 직접 POST | 외부 시스템 → Flow → Teams |
| 메시지 형식 | 주로 MessageCard | Adaptive Card 권장 |
| 데이터 가공·분기 | 거의 불가 | 조건, 변환, 승인, 다중 채널 전송 가능 |
| 인증 설정 | URL 보유자가 호출 | 익명/테넌트/특정 사용자 선택 가능 |
| 운영 관리 | 채널 Connector 중심 | Flow 소유자·공동 소유자 중심 |
| 선택 이유 | 단순하지만 은퇴 대상 | Microsoft의 현재 대체 경로 |
💡 Power Automate는 단순한 "새 URL"이 아니라, webhook을 트리거로 사용하는 자동화 Flow입니다.
이번 요구사항에는 Power Automate가 가장 자연스럽습니다.
✅ 따라서 현재 구조는 **"Teams Connector 은퇴 대응 + Microsoft 1st-party만 사용"**이라는 제약에 맞습니다.
Power Automate에서 자동화된 클라우드 Flow를 만들고 트리거를 추가합니다.
Teams 웹후크 요청이 수신된 경우
트리거의 **"누가 흐름을 트리거할 수 있나요?"**에는 다음 선택지가 있습니다.
| 선택지 | 의미 | GitHub Actions에 적합한가 |
|---|---|---|
| 내 테넌트의 모든 사용자 | 회사 Microsoft 계정 OAuth 필요 | 아니오 |
| 내 테넌트의 특정 사용자 | 지정 사용자/서비스 주체 OAuth 필요 | 아니오 |
| 모든 사용자 | URL만으로 POST 가능 | 예 |
이번 자동화는 세 번째 모든 사용자를 선택했습니다.
🔐 여기서 "모든 사용자"는 URL을 아는 시스템이 POST할 수 있다는 뜻입니다. 따라서 webhook URL은 GitHub Secret에 저장하고 코드·문서·채팅에 노출하지 않는 것이 원칙입니다.
Teams webhook 트리거는 호출자를 누구나, 테넌트 사용자, 특정 사용자로 제한할 수 있습니다. Teams 커넥터 문서
다음 액션을 추가합니다.
채팅 또는 채널에서 카드 게시
설정값:
게시자: Flow bot
게시 위치: 채널
팀: Technology
채널: GSC 리포트
카드: 첨부 파일 적응형 카드첨부 파일 적응형 카드를 선택하면 Power Automate는 받은 카드 배열을 하나씩 처리하도록 For each를 자동 생성할 수 있습니다.
기존 O365 Incoming Webhook에서 많이 쓰던 카드 형식입니다.
{
"@type": "MessageCard",
"summary": "GSC 일일 리포트"
}기존 GSC 스크립트도 처음에는 MessageCard를 만들었습니다.
Teams와 Power Automate에서 권장되는 범용 카드 형식입니다.
{
"type": "AdaptiveCard",
"version": "1.4",
"body": []
}이번 Flow의 트리거와 카드 게시 액션이 Adaptive Card attachment를 중심으로 동작하므로, GSC 스크립트도 Adaptive Card 형식으로 변경했습니다.
현재 scripts/gsc-report.mjs는 다음 구조를 만듭니다.
Webhook 요청
└─ attachments
└─ Adaptive Card
├─ 리포트 제목
├─ 기준일
└─ FactSet
├─ 노출수
├─ 클릭수
├─ CTR
└─ 평균순위scripts/gsc-report.mjs의 역할은 네 가지입니다.
⏱️ GSC 데이터는 실시간 데이터가 아닙니다. 그래서 스크립트는 안전하게 실행일 기준 3일 전을 기준일로 잡습니다.
오늘: 7월 15일
리포트 기준일: 7월 12일
비교일: 7월 11일GitHub Actions는 .github/workflows/gsc-report.yml의 cron에 따라 실행됩니다.
필요한 GitHub Repository Secret은 아래 2개 입니다.
GitHub Actions를 실행하기 전에 curl로 webhook 연결을 먼저 검증했습니다.
성공한 요청은 아래와 같은 형태입니다.
curl -i -X POST \
'https://.../invoke?api-version=2024-10-01&sp=...&sv=1.0&sig=...' \
-H 'Content-Type: application/json' \
--data '{
"type": "message",
"attachments": [
{
"contentType": "application/vnd.microsoft.card.adaptive",
"contentUrl": null,
"content": {
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": "GSC webhook 테스트 성공"
}
]
}
}
]
}'성공 시 HTTP 응답은 다음과 같습니다.
HTTP/2 202💡 202 Accepted는 Power Automate가 요청을 접수했고 Flow 실행을 시작했다는 뜻입니다. Teams 카드 게시까지 완료됐는지는 채널 또는 Flow 실행 기록에서 확인합니다.
DirectApiAuthorizationRequiredOAuth authorization scheme is required원인
해결
ApiVersionInvalidAPI version must be specified...원인
api-version=1이 현재 endpoint에서 지원되지 않음해결
api-version=2024-10-01으로 변경했습니다.
401 AuthenticationFailedmissing or empty Authorization header처음에는 익명 호출 설정 문제로 보였지만, 실제 원인은 curl URL의 잘못된 이스케이프였습니다.
잘못된 예:
'https://...?api-version=2024-10-01&sp\=...&sig\=...'⚠️ 작은따옴표 안의 백슬래시(
\)는 shell에서 제거되지 않고 URL에 그대로 포함됩니다. 따라서sp,sv,sig파라미터 이름이 손상됩니다.
올바른 예:
'https://...?api-version=2024-10-01&sp=...&sv=1.0&sig=...'작은따옴표로 URL 전체를 감쌌다면 ?, =, & 앞에 백슬래시를 붙이지 않습니다.
curl: (2) no URL specified원인
curl -i -X POST
'https://...'처럼 POST 뒤에서 줄바꿈해 명령이 끝난 경우입니다.
해결
curl -i -X POST \
'https://...' \처럼 줄 끝에 \를 넣어 같은 명령으로 연결합니다.
원인
해결
"채팅 또는 채널에서 카드 게시" 액션을 선택합니다. 새 카드 게시 액션에는 기존 메시지 ID가 필요하지 않습니다.
현재처럼 익명 webhook이 허용되면 OAuth는 필요 없습니다.
다만 보안 정책상 "모든 사용자"를 사용할 수 없는 회사 환경이라면 OAuth를 사용합니다.
GitHub Actions
→ Entra ID에서 access token 발급
→ Authorization: Bearer <token>
→ Power Automate Flow이때 Flow는 "내 테넌트의 특정 사용자"로 만들고, Entra 서비스 주체의 Object ID를 허용 사용자로 등록합니다. Power Automate는 이 시나리오를 지원합니다. Power Automate OAuth 트리거 문서
✅ 이번 구성에서는 익명 webhook 호출이 실제로 202와 Teams 카드 표시까지 성공했으므로 OAuth는 사용하지 않습니다.
🗒️ - webhook URL은 비밀번호처럼 다룬다.
URL을 GitHub Secret에만 저장한다.
202은 요청 접수 성공이고, 카드 게시 실패 여부는 Power Automate 실행 기록에서 확인한다.GSC 데이터는 2~3일 늦게 확정되는 것이 정상이다.
Flow는 개인 계정에 연결될 수 있으므로, 운영 담당자를 공동 소유자로 추가하는 것을 권장한다.
Teams 카드 형식은 향후에도 Adaptive Card를 기준으로 유지한다.