티스토리 뷰

반응형

GitHub Webhook을 활용한 NestJS 서버 구축 및 이벤트 처리

이전 글에서 GitHub Webhook의 개념과 활용 사례를 살펴보았습니다.
이번 글에서는 NestJS를 사용하여 Webhook 서버를 구축하고, GitHub에서 발생하는 이벤트를 처리하는 방법을 설명합니다.


1. 프로젝트 환경 설정 (NestJS + Webhook 서버 구축)

🔹 1-1. NestJS 프로젝트 생성

npm install -g @nestjs/cli
nest new github-webhook-server
cd github-webhook-server

🔹 1-2. Webhook을 수신할 컨트롤러 생성

nest generate controller webhook

src/webhook/webhook.controller.ts 파일이 생성됩니다.


2. Webhook 요청을 처리하는 API 구현

반응형

GitHub Webhook 요청을 수신하는 POST 엔드포인트를 구현합니다.

🔹 2-1. Webhook 컨트롤러 코드 작성

import { Controller, Post, Headers, Body } from '@nestjs/common';

@Controller('webhook')
export class WebhookController {
  @Post()
  handleWebhook(
    @Headers('x-github-event') event: string,
    @Headers('x-hub-signature-256') signature: string,
    @Body() payload: any,
  ) {
    console.log(`📌 GitHub Event: ${event}`);
    console.log(`🔹 Payload:`, JSON.stringify(payload, null, 2));
    return { message: 'Webhook received' };
  }
}

🚀 x-github-event 헤더를 이용해 이벤트 종류를 식별할 수 있습니다.

🔹 2-2. 서버 실행

npm run start

이제 http://localhost:3000/webhook 엔드포인트에서 Webhook을 받을 준비가 되었습니다.


3. GitHub Webhook 등록 및 테스트

🔹 3-1. GitHub Webhook 설정 방법

  1. GitHub Repository → Settings → Webhooks로 이동
  2. "Add webhook" 버튼 클릭
  3. Payload URL: http://your-server/webhook 입력
  4. Content type: application/json 선택
  5. Secret: 랜덤한 시크릿 키 설정 (보안용)
  6. Events: Push, Pull Request, Issue 등 원하는 이벤트 선택
  7. "Add webhook" 클릭하여 저장

이제 Webhook이 설정되었으며, GitHub에서 이벤트가 발생하면 서버로 자동 요청이 전송됩니다.


4. Webhook 보안 강화 (Signature 검증)

GitHub Webhook 요청이 **정상적인 요청인지 검증하기 위해 X-Hub-Signature-256**을 활용한 HMAC 검증을 추가해야 합니다.

🔹 4-1. HMAC 검증 함수 추가

src/utils/verifySignature.ts 파일을 생성하고, 다음과 같이 구현합니다.

import * as crypto from 'crypto';

export function verifySignature(payload: string, signature: string, secret: string): boolean {
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(payload, 'utf-8');
  const expectedSignature = `sha256=${hmac.digest('hex')}`;
  return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature));
}

🔹 4-2. 컨트롤러에서 검증 로직 추가

import { Controller, Post, Headers, Body, BadRequestException } from '@nestjs/common';
import { verifySignature } from '../utils/verifySignature';

const GITHUB_SECRET = process.env.GITHUB_SECRET || 'your_secret_key';

@Controller('webhook')
export class WebhookController {
  @Post()
  handleWebhook(
    @Headers('x-github-event') event: string,
    @Headers('x-hub-signature-256') signature: string,
    @Body() payload: any,
  ) {
    const payloadString = JSON.stringify(payload);
    if (!verifySignature(payloadString, signature, GITHUB_SECRET)) {
      throw new BadRequestException('Invalid signature');
    }

    console.log(`📌 Valid Webhook Event: ${event}`);
    return { message: 'Webhook processed successfully' };
  }
}

💡 verifySignature()를 사용하여 Webhook 요청이 유효한지 검증합니다.
잘못된 Webhook 요청은 400 Bad Request 오류를 반환합니다.


5. GitHub Webhook 이벤트 처리 (Push, PR, Issue)

🔹 5-1. 이벤트별 처리 로직 추가

Webhook 이벤트에 따라 Push, PR, Issue 이벤트를 다르게 처리합니다.

import { Controller, Post, Headers, Body } from '@nestjs/common';

@Controller('webhook')
export class WebhookController {
  @Post()
  handleWebhook(@Headers('x-github-event') event: string, @Body() payload: any) {
    switch (event) {
      case 'push':
        console.log(`📌 Push Event: ${payload.ref}에 커밋 발생`);
        break;
      case 'pull_request':
        console.log(`📌 Pull Request ${payload.action}: #${payload.number}`);
        break;
      case 'issues':
        console.log(`📌 Issue ${payload.action}: #${payload.issue.number}`);
        break;
      default:
        console.log(`📌 Unknown Event: ${event}`);
    }
    return { message: `Processed ${event} event` };
  }
}

🚀 Push, Pull Request, Issue 이벤트 발생 시 각각 다른 메시지가 출력됩니다.


6. Webhook 로그 저장 및 데이터베이스 연동

🔹 6-1. PostgreSQL 설정 (Prisma ORM 사용)

npm install @prisma/client @nestjs/prisma

🔹 6-2. Prisma 모델 정의 (prisma/schema.prisma)

model WebhookEvent {
  id        String @id @default(uuid())
  eventType String
  payload   Json
  createdAt DateTime @default(now())
}

🔹 6-3. Webhook 로그 저장 코드 추가

import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';

@Injectable()
export class WebhookService {
  constructor(private readonly prisma: PrismaService) {}

  async saveWebhookEvent(eventType: string, payload: any) {
    return this.prisma.webhookEvent.create({
      data: { eventType, payload },
    });
  }
}

🔹 6-4. 컨트롤러에서 DB 저장 로직 적용

@Controller('webhook')
export class WebhookController {
  constructor(private readonly webhookService: WebhookService) {}

  @Post()
  async handleWebhook(@Headers('x-github-event') event: string, @Body() payload: any) {
    await this.webhookService.saveWebhookEvent(event, payload);
    console.log(`✅ Event Saved: ${event}`);
    return { message: 'Webhook processed and saved' };
  }
}

💡 이제 Webhook 이벤트가 발생하면 PostgreSQL에 자동 저장됩니다.


7. 마무리 및 다음 글 예고

이번 글에서는 GitHub Webhook을 수신하는 NestJS 서버를 구축하고, 이벤트 처리 및 로그 저장 기능을 구현했습니다.
다음 글에서는 Webhook을 활용한 CI/CD 자동화 및 배포 트리거를 다뤄보겠습니다! 🚀


 

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함
반응형