WEB HOOK 관련

WEBHOOK이란?

특정 이벤트(트리거) 시에 외부에 이벤트를 전달하는 gitlab 자체기능, push, issue 등 다양한 이벤트에 대해서 외부로 api를 날릴 수 있다. github action에서는 cicd pipeline으로 구축했지만 gitlab의 경우 자체기능으로 갖고 있다.

목표

issue가 발생할 때 issue의 create, update, closed 시 외부로 api를 송신해 보자

설정

1. webhooks 설정

settings 안에 webhooks에서 설정이 가능하다. 수신받을 url 을 입력하고 아래 trigger를 상황에 맞게 체크하면 끝이다.

2. 수신부

테스트 용으로 vercel에서 nextjs boiler plate를 이용했다.

// app/api/receive-issue/route.js
import { NextResponse } from 'next/server';

// 간단한 메모리 내 저장소 (실제 서비스에서는 데이터베이스 사용 권장)
let receivedIssues:any[] = [];

export async function POST(request:any) {
  // Secret Token 검증 (옵션)
  const gitlabToken = request.headers.get('x-gitlab-token');
  if (process.env.GITLAB_SECRET_TOKEN && gitlabToken !== process.env.GITLAB_SECRET_TOKEN) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }

  // GitLab에서 전송한 JSON 페이로드 파싱
  const payload = await request.json();
  console.log('Received GitLab issue payload:', payload);

  // 수신한 이슈 데이터를 메모리 저장소에 추가 (서버 재시작 시 초기화됨)
  receivedIssues.push(payload);

  return NextResponse.json({ message: 'Issue received successfully' });
}

export async function GET(request:any) {
  // 저장된 이슈 데이터를 반환
  console.log('logging')
  return NextResponse.json({ issues: receivedIssues });
}

수신 프로젝트의 호출부

// app/issues/page.js
'use client';

import useSWR from 'swr';

const fetcher = (url:string) => fetch(url).then(res => res.json());

export default function IssuesPage() {
  const { data, error } = useSWR('/api/receive-issue', fetcher);

  if (error) return <div>이슈 정보를 불러오는데 에러가 발생했습니다.</div>;
  if (!data) return <div>로딩 중...</div>;

  return (
    <div>
      <h1>수신된 이슈 목록</h1>
      {data.issues.length === 0 ? (
        <p>아직 이슈가 수신되지 않았습니다.</p>
      ) : (
        <ul>
          {data.issues.map((issue:any, idx:any) => (
            <li key={idx}>
              <h3>{issue.object_attributes.title}</h3>
              <p>{issue.object_attributes.description}</p>
              <small>이슈 번호: {issue.object_attributes.iid}</small>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

화면

간단한 이슈를 Closes #1 로 1번 이슈를 종료해봤다.

Response Body 형태

자세한 body의 형태는 아래 document를 참고하면 된다. https://docs.gitlab.com/user/project/integrations/webhook_events/

header

X-Gitlab-Event: Issue Hook

payload

{
  "object_kind": "issue",
  "event_type": "issue",
  "user": {
    "id": 1,
    "name": "Administrator",
    "username": "root",
    "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon",
    "email": "admin@example.com"
  },
  "project": {
    "id": 1,
    "name":"Gitlab Test",
    "description":"Aut reprehenderit ut est.",
    "web_url":"http://example.com/gitlabhq/gitlab-test",
    "avatar_url":null,
    "git_ssh_url":"git@example.com:gitlabhq/gitlab-test.git",
    "git_http_url":"http://example.com/gitlabhq/gitlab-test.git",
    "namespace":"GitlabHQ",
    "visibility_level":20,
    "path_with_namespace":"gitlabhq/gitlab-test",
    "default_branch":"master",
    "ci_config_path": null,
    "homepage":"http://example.com/gitlabhq/gitlab-test",
    "url":"http://example.com/gitlabhq/gitlab-test.git",
    "ssh_url":"git@example.com:gitlabhq/gitlab-test.git",
    "http_url":"http://example.com/gitlabhq/gitlab-test.git"
  },
  "object_attributes": {
    "id": 301,
    "title": "New API: create/update/delete file",
    "assignee_ids": [51],
    "assignee_id": 51,
    "author_id": 51,
    "project_id": 14,
    "created_at": "2013-12-03T17:15:43Z",
    "updated_at": "2013-12-03T17:15:43Z",
    "updated_by_id": 1,
    "last_edited_at": null,
    "last_edited_by_id": null,
    "relative_position": 0,
    "description": "Create new API for manipulations with repository",
    "milestone_id": null,
    "state_id": 1,
    "confidential": false,
    "discussion_locked": true,
    "due_date": null,
    "moved_to_id": null,
    "duplicated_to_id": null,
    "time_estimate": 0,
    "total_time_spent": 0,
    "time_change": 0,
    "human_total_time_spent": null,
    "human_time_estimate": null,
    "human_time_change": null,
    "weight": null,
    "health_status": "at_risk",
    "type": "Issue",
    "iid": 23,
    "url": "http://example.com/diaspora/issues/23",
    "state": "opened",
    "action": "open",
    "severity": "high",
    "escalation_status": "triggered",
    "escalation_policy": {
      "id": 18,
      "name": "Engineering On-call"
    },
    "labels": [{
        "id": 206,
        "title": "API",
        "color": "#ffffff",
        "project_id": 14,
        "created_at": "2013-12-03T17:15:43Z",
        "updated_at": "2013-12-03T17:15:43Z",
        "template": false,
        "description": "API related issues",
        "type": "ProjectLabel",
        "group_id": 41
      }]
  },
  "repository": {
    "name": "Gitlab Test",
    "url": "http://example.com/gitlabhq/gitlab-test.git",
    "description": "Aut reprehenderit ut est.",
    "homepage": "http://example.com/gitlabhq/gitlab-test"
  },
  "assignees": [{
    "name": "User1",
    "username": "user1",
    "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
  }],
  "assignee": {
    "name": "User1",
    "username": "user1",
    "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40\u0026d=identicon"
  },
  "labels": [{
    "id": 206,
    "title": "API",
    "color": "#ffffff",
    "project_id": 14,
    "created_at": "2013-12-03T17:15:43Z",
    "updated_at": "2013-12-03T17:15:43Z",
    "template": false,
    "description": "API related issues",
    "type": "ProjectLabel",
    "group_id": 41
  }],
  "changes": {
    "updated_by_id": {
      "previous": null,
      "current": 1
    },
    "updated_at": {
      "previous": "2017-09-15 16:50:55 UTC",
      "current": "2017-09-15 16:52:00 UTC"
    },
    "labels": {
      "previous": [{
        "id": 206,
        "title": "API",
        "color": "#ffffff",
        "project_id": 14,
        "created_at": "2013-12-03T17:15:43Z",
        "updated_at": "2013-12-03T17:15:43Z",
        "template": false,
        "description": "API related issues",
        "type": "ProjectLabel",
        "group_id": 41
      }],
      "current": [{
        "id": 205,
        "title": "Platform",
        "color": "#123123",
        "project_id": 14,
        "created_at": "2013-12-03T17:15:43Z",
        "updated_at": "2013-12-03T17:15:43Z",
        "template": false,
        "description": "Platform related issues",
        "type": "ProjectLabel",
        "group_id": 41
      }]
    }
  }
}