BE 공부/Infrastructure
[GitHub] Webhook을 활용한 자동 배포 트리거 시스템 구축하기
꼬질꼬질두부
2025. 6. 19. 17:51
반응형
GitHub Webhook을 활용한 자동 배포 트리거 시스템 구축하기
1. 개요
CI/CD를 구성하면서 이런 생각 해본 적 있으신가요?
"GitHub에 push하면 자동으로 배포까지 이어지면 좋겠다!"
많은 조직에서는 Jenkins, GitHub Actions 등으로 배포를 자동화합니다. 하지만 Webhook만으로도 간단한 자동 배포 트리거 시스템을 구축할 수 있습니다.
이 글에서는 GitHub Webhook + AWS API Gateway + AWS Lambda를 활용하여 다음과 같은 기능을 구현합니다.
- GitHub push 이벤트 발생 시
- Webhook이 API Gateway를 통해 Lambda를 호출
- Lambda는 서명을 검증한 뒤
- 조건에 맞으면 배포를 트리거(Lambda invoke 또는 SQS 전송)
2. Webhook이란?
Webhook은 특정 이벤트가 발생했을 때 HTTP 요청을 외부로 보내는 기능입니다.
GitHub에서는 다음과 같은 이벤트에 대해 Webhook을 설정할 수 있습니다:
- Push
- Pull Request
- Release
- Issue 등
Webhook은 이벤트 정보를 JSON payload 형태로 전송합니다.
3. 시스템 구성도
GitHub (Push)
↓ Webhook
API Gateway (Webhook Endpoint)
↓
AWS Lambda (서명 검증 + 배포 트리거)
↓
AWS Lambda 또는 SQS로 배포 트리거
4. GitHub Webhook 설정
- GitHub 리포지토리 > Settings > Webhooks > Add webhook
- Payload URL: https://{your-api-gateway}/github-webhook
- Content type: application/json
- Secret: your-webhook-secret (Lambda에서 검증용으로 사용)
- Trigger: Just the push event
5. Lambda 함수 코드 (TypeScript)
5.1 모듈 및 환경변수 설정
import crypto from 'crypto';
import { APIGatewayProxyHandler } from 'aws-lambda';
import { LambdaClient, InvokeCommand } from '@aws-sdk/client-lambda';
// GitHub Webhook에서 설정한 Secret을 환경변수로 가져옵니다.
const SECRET = process.env.GITHUB_WEBHOOK_SECRET!;
- crypto는 GitHub의 서명 검증에 사용됩니다.
- @aws-sdk/client-lambda는 AWS SDK v3로 Lambda를 호출할 수 있게 해줍니다.
- SECRET은 GitHub Webhook 설정에서 입력한 값을 동일하게 사용해야 서명 검증이 통과됩니다.
5.2 핸들러 함수 전체 구조
export const githubWebhookHandler: APIGatewayProxyHandler = async (event) => {
const signature = event.headers['x-hub-signature-256']; // GitHub에서 전송된 서명
const payload = event.body ?? ''; // 본문(payload) 문자열
// 1. 서명 검증
const isValid = verifySignature(SECRET, payload, signature);
if (!isValid) {
return { statusCode: 401, body: 'Invalid signature' };
}
// 2. payload를 JSON으로 파싱
const body = JSON.parse(payload);
const branch = body.ref; // 예: 'refs/heads/main'
// 3. main 브랜치에만 반응
if (branch !== 'refs/heads/main') {
return { statusCode: 200, body: 'Ignored: not main branch' };
}
// 4. 배포용 Lambda 호출
const lambda = new LambdaClient({});
const command = new InvokeCommand({
FunctionName: 'deploy-my-service',
InvocationType: 'Event', // 비동기 호출
Payload: Buffer.from(JSON.stringify({ repo: body.repository.full_name })),
});
await lambda.send(command);
return { statusCode: 200, body: 'Deployment triggered' };
};
동작 설명
- 서명 검증: Webhook을 가장한 외부 요청을 차단하기 위해 서명을 반드시 검증해야 합니다.
- 브랜치 필터링: push는 여러 브랜치에서 발생할 수 있으므로 main 브랜치로 한정합니다.
- Lambda 호출: 실제 배포를 실행하는 Lambda 함수를 비동기로 호출합니다.
예시 payload 구조 (일부)
{
"ref": "refs/heads/main",
"repository": {
"full_name": "my-org/my-repo"
},
"pusher": {
"name": "user123"
}
}
5.3 서명 검증 함수
function verifySignature(secret: string, payload: string, signature: string | undefined): boolean {
if (!signature) return false; // 서명이 없으면 무조건 실패
const hmac = crypto.createHmac('sha256', secret);
const digest = `sha256=${hmac.update(payload).digest('hex')}`;
return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signature));
}
동작 설명
- crypto.createHmac()을 통해 요청 payload에 대해 해시를 생성합니다.
- GitHub에서 보낸 서명(x-hub-signature-256)과 해시값을 비교하여 검증합니다.
- timingSafeEqual은 시간 기반의 공격을 방지하기 위해 사용합니다.
해시 알고리즘(sha256)과 x-hub-signature-256 헤더는 반드시 일치해야 합니다.
위 코드 예시는 전체적인 동작 방식을 간단히 설명하기 위한 것이며, 실무에 적용할 때에는 다양한 요소들을 고려하여 직접 구현해야합니다!
참고 자료
반응형