Skip to main content

웹후크 배달 처리

웹후크 배달을 수신 대기하고 그에 응답하는 코드를 작성하는 방법을 알아봅니다.

소개

웹후크를 만들 때 URL을 지정하고 이벤트 유형을 구독합니다. 웹후크가 구독하는 이벤트가 발생하는 경우 GitHub는 이벤트에 대한 데이터가 포함된 HTTP 요청을 지정한 URL로 보냅니다. 서버가 해당 URL에서 웹후크 배달을 수신 대기하도록 설정되어 있는 경우 수신 시 작업을 수행할 수 있습니다.

이 문서에서는 서버가 웹후크 배달을 수신 대기하고 그에 응답할 수 있도록 코드를 작성하는 방법을 설명합니다. 컴퓨터 또는 codespace를 로컬 서버로 사용하여 코드를 테스트합니다.

설정

웹후크를 로컬로 테스트하려면 웹후크 프록시 URL을 사용하여 웹후크를 GitHub에서 컴퓨터 또는 codespace로 전달할 수 있습니다. 이 문서에서는 smee.io를 사용하여 웹후크 프록시 URL을 제공하고 웹후크를 전달합니다.

웹후크 프록시 URL 가져오기

  1. 브라우저에서 https://smee.io/로 이동합니다.
  2. 새 채널 시작을 클릭합니다.
  3. "웹후크 프록시 URL" 아래에 전체 URL을 복사합니다. 다음 설정 단계에서 이 URL을 사용합니다.

웹후크 전달

  1. smee-client가 아직 설치되어 있지 않은 경우 터미널에서 다음 명령을 실행합니다.

    Shell
    npm install --global smee-client
    
  2. smee.io에서 전달한 웹후크를 수신하려면 터미널에서 다음 명령을 실행합니다. WEBHOOK_PROXY_URL을 앞서 복사한 웹후크 프록시 URL로 바꿉니다.

    Shell
    smee --url WEBHOOK_PROXY_URL --path /webhook --port 3000
    

    다음과 같은 출력이 표시됩니다. 여기서 WEBHOOK_PROXY_URL은 웹후크 프록시 URL입니다.

    Shell
    Forwarding WEBHOOK_PROXY_URL to http://127.0.0.1:3000/webhook
    Connected WEBHOOK_PROXY_URL
    

    경로는 /webhook이고 포트는 3000입니다. 나중에 웹후크 배달을 처리하는 코드를 작성할 때 이 값을 사용합니다.

  3. 웹후크를 테스트하는 동안 계속 실행해 둡니다. 웹후크 전달을 중지하려면 Ctrl+C를 입력합니다.

웹후크 만들기

  1. 다음 설정을 사용하여 웹후크를 만듭니다. 자세한 내용은 "웹후크 만들기"을(를) 참조하세요.

    • URL은 앞서 복사한 웹후크 프록시 URL을 사용합니다.
    • 콘텐츠 유형을 선택할 수 있는 옵션이 있는 경우 JSON을 사용합니다.

웹후크 배달을 처리하는 코드 작성

웹후크 배달을 처리하려면 다음을 수행하는 코드를 작성해야 합니다.

  • 웹후크 URL에 대한 요청을 수신 대기하도록 서버 초기화
  • 요청에서 HTTP 헤더 및 본문 읽기
  • 요청에 대한 응답으로 원하는 작업 수행

서버에서 실행할 수 있는 모든 프로그래밍 언어를 사용할 수 있습니다.

다음 예에서는 웹후크 배달이 수신되면 메시지를 출력합니다. 그러나 코드를 수정하여 GitHub API에 요청하거나 Slack 메시지를 보내는 등 다른 작업을 수행할 수 있습니다.

Ruby 예제

이 예에서는 Ruby gem인 Sinatra를 사용하여 경로를 정의하고 HTTP 요청을 처리합니다. 자세한 내용은 Sinatra 추가 정보를 참조하세요.

Ruby 예제: 종속성 설치

이 예를 사용하려면 Ruby 프로젝트에 sinatra gem을 설치해야 합니다. 예를 들면 Bundler를 사용하여 설치할 수 있습니다.

  1. Bundler가 아직 설치되어 있지 않은 경우 해당 터미널에서 다음 명령을 실행합니다.

    Shell
    gem install bundler
    
  2. 앱에 Gemfile이 없는 경우 터미널에서 다음 명령을 실행합니다.

    Shell
    bundle init
    
  3. 앱에 Gemfile.lock이 없는 경우 터미널에서 다음 명령을 실행합니다.

    Shell
    bundle install
    
  4. 터미널에서 다음 명령을 실행하여 Sinatra gem을 설치합니다.

    Shell
    bundle add sinatra
    

Ruby 예제: 코드 작성

다음 내용으로 Ruby 파일을 만듭니다. 웹후크가 구독하는 이벤트 유형 및 웹후크를 만들 때 GitHub가 보내는 ping 이벤트를 처리하도록 코드를 수정합니다. 이 예에서는 issuesping 이벤트를 처리합니다.

Ruby
require 'sinatra'
require 'json'

These are the dependencies for this code. You installed the sinatra gem earlier. For more information, see "Ruby example: Install dependencies." The json library is a standard Ruby library, so you don't need to install it.

post '/webhook' do

The /webhook route matches the path that you specified for the smee.io forwarding. For more information, see "Forward webhooks."

Once you deploy your code to a server and update your webhook URL, you should change this to match the path portion of the URL for your webhook.

  status 202

Respond to indicate that the delivery was successfully received. Your server should respond with a 2XX response within 10 seconds of receiving a webhook delivery. If your server takes longer than that to respond, then GitHub terminates the connection and considers the delivery a failure.

  github_event = request.env['HTTP_X_GITHUB_EVENT']

Check the X-GitHub-Event header to learn what event type was sent. Sinatra changes X-GitHub-Event to HTTP_X_GITHUB_EVENT.

  if github_event == "issues"
    data = JSON.parse(request.body.read)
    action = data['action']
    if action == "opened"
      puts "An issue was opened with this title: #{data['issue']['title']}"
    elsif action == "closed"
      puts "An issue was closed by #{data['issue']['user']['login']}"
    else
      puts "Unhandled action for the issue event: #{action}"
    end
  elsif github_event == "ping"
    puts "GitHub sent the ping event"
  else
    puts "Unhandled event: #{github_event}"
  end
end

You should add logic to handle each event type that your webhook is subscribed to. For example, this code handles the issues and ping events.

If any events have an action field, you should also add logic to handle each action that you are interested in. For example, this code handles the opened and closed actions for the issue event.

For more information about the data that you can expect for each event type, see "웹후크 이벤트 및 페이로드."

# These are the dependencies for this code. You installed the `sinatra` gem earlier. For more information, see "[Ruby example: Install dependencies](#ruby-example-install-dependencies)." The `json` library is a standard Ruby library, so you don't need to install it.
require 'sinatra'
require 'json'

# The `/webhook` route matches the path that you specified for the smee.io forwarding. For more information, see "[Forward webhooks](#forward-webhooks)."
#
# Once you deploy your code to a server and update your webhook URL, you should change this to match the path portion of the URL for your webhook.
post '/webhook' do

  # Respond to indicate that the delivery was successfully received.
  # Your server should respond with a 2XX response within 10 seconds of receiving a webhook delivery. If your server takes longer than that to respond, then GitHub terminates the connection and considers the delivery a failure.
  status 202

  # Check the `X-GitHub-Event` header to learn what event type was sent.
  # Sinatra changes `X-GitHub-Event` to `HTTP_X_GITHUB_EVENT`.
  github_event = request.env['HTTP_X_GITHUB_EVENT']

  # You should add logic to handle each event type that your webhook is subscribed to.
  # For example, this code handles the `issues` and `ping` events.
  #
  # If any events have an `action` field, you should also add logic to handle each action that you are interested in.
  # For example, this code handles the `opened` and `closed` actions for the `issue` event.
  #
  # For more information about the data that you can expect for each event type, see "[AUTOTITLE](/webhooks/webhook-events-and-payloads)."
  if github_event == "issues"
    data = JSON.parse(request.body.read)
    action = data['action']
    if action == "opened"
      puts "An issue was opened with this title: #{data['issue']['title']}"
    elsif action == "closed"
      puts "An issue was closed by #{data['issue']['user']['login']}"
    else
      puts "Unhandled action for the issue event: #{action}"
    end
  elsif github_event == "ping"
    puts "GitHub sent the ping event"
  else
    puts "Unhandled event: #{github_event}"
  end
end

Ruby 예제: 코드 테스트

웹후크를 테스트하려면 컴퓨터 또는 codespace를 로컬 서버 역할로 사용할 수 있습니다. 이 단계에 문제가 있는 경우 문제 해결을 참조하세요.

  1. 웹후크를 제공하고 있는지 확인합니다. 더 이상 웹후크를 전달하지 않는 경우 웹후크 전달의 단계를 다시 따릅니다.

  2. 별도의 터미널 창에서 다음 명령을 실행하여 컴퓨터 또는 codespace에서 로컬 서버를 시작합니다. FILE_PATH를 이전 섹션의 코드가 저장된 파일의 경로로 바꿉니다. PORT=3000은 이전 단계에서 웹후크 전달에 지정한 포트와 일치합니다.

    Shell
    PORT=3000 ruby FILE_NAME
    

    "Sinatra가 3000에서 스테이징되었습니다."와 같은 출력이 표시되어야 합니다.

  3. 웹후크를 트리거합니다. 예를 들어 issues 이벤트를 구독하는 리포지토리 웹후크를 만든 경우 리포지토리에서 이슈를 엽니다. 이전 웹후크 배달을 다시 배달할 수도 있습니다. 자세한 내용은 "웹후크 다시 제공"을(를) 참조하세요.

  4. smee.io에서 웹후크 프록시 URL로 이동합니다. 트리거하거나 다시 배달한 이벤트에 해당하는 이벤트가 표시되어야 합니다. 이는 GitHub가 웹후크 배달을 지정한 페이로드 URL로 보냈음을 나타냅니다.

  5. smee --url WEBHOOK_PROXY_URL --path /webhook --port 3000을(를) 실행한 터미널 창에 POST http://127.0.0.1:3000/webhook - 202와 같은 내용이 표시되어야 합니다. 이는 smee가 웹후크를 로컬 서버로 전달했음을 나타냅니다.

  6. PORT=3000 ruby FILE_NAME을(를) 실행한 터미널 창에 전송한 이벤트에 해당하는 메시지가 표시되어야 합니다. 예를 들어 위의 코드 예를 사용하고 ping 이벤트를 다시 배달한 경우 "GitHub가 ping 이벤트를 보냈습니다"가 표시되어야 합니다. Sinatra가 자동으로 출력하는 다른 줄도 표시될 수 있습니다.

  7. 두 터미널 창 모두에서 Ctrl+C 를 입력하여 로컬 서버를 중지하고 전달된 웹후크 수신 대기를 중지합니다.

이제 코드를 로컬로 테스트했으므로 프로덕션에서 웹후크를 사용하도록 변경할 수 있습니다. 자세한 내용은 “다음 단계”를 참조하세요. 코드를 테스트하는 데 문제가 있는 경우 "문제 해결"의 단계를 시도해 보세요.

JavaScript 예제

이 예에서는 Node.js 및 Express 라이브러리를 사용하여 경로를 정의하고 HTTP 요청을 처리합니다. 자세한 내용은 “expressjs.com”을 참조하세요.

GitHub의 Octokit.js SDK를 사용하는 예는 “웹후크 이벤트에 응답하는 GitHub 앱 빌드”를 참조하세요.

이 예에서 Node.js 버전 12 이상 및 npm 버전 6.12.0 이상을 실행하려면 컴퓨터 또는 codespace가 필요합니다. 자세한 내용은 Node.js를 참조하세요.

JavaScript 예제: 종속성 설치

이 예를 사용하려면 Node.js 프로젝트에 express 라이브러리를 설치해야 합니다. 예시:

Shell
npm install express

JavaScript 예제: 코드 작성

다음 내용으로 JavaScript 파일을 만듭니다. 웹후크가 구독하는 이벤트 유형 및 웹후크를 만들 때 GitHub가 보내는 ping 이벤트를 처리하도록 코드를 수정합니다. 이 예에서는 issuesping 이벤트를 처리합니다.

JavaScript
const express = require('express');

You installed the express library earlier. For more information, see "JavaScript example: Install dependencies."

const app = express();

This initializes a new Express application.

app.post('/webhook', express.json({type: 'application/json'}), (request, response) => {

This defines a POST route at the /webhook path. This path matches the path that you specified for the smee.io forwarding. For more information, see "Forward webhooks."

Once you deploy your code to a server and update your webhook URL, you should change this to match the path portion of the URL for your webhook.

  response.status(202).send('Accepted');

Respond to indicate that the delivery was successfully received. Your server should respond with a 2XX response within 10 seconds of receiving a webhook delivery. If your server takes longer than that to respond, then GitHub terminates the connection and considers the delivery a failure.

  const githubEvent = request.headers['x-github-event'];

Check the x-github-event header to learn what event type was sent.

  if (githubEvent === 'issues') {
    const data = request.body;
    const action = data.action;
    if (action === 'opened') {
      console.log(`An issue was opened with this title: ${data.issue.title}`);
    } else if (action === 'closed') {
      console.log(`An issue was closed by ${data.issue.user.login}`);
    } else {
      console.log(`Unhandled action for the issue event: ${action}`);
    }
  } else if (githubEvent === 'ping') {
    console.log('GitHub sent the ping event');
  } else {
    console.log(`Unhandled event: ${githubEvent}`);
  }
});

You should add logic to handle each event type that your webhook is subscribed to. For example, this code handles the issues and ping events.

If any events have an action field, you should also add logic to handle each action that you are interested in. For example, this code handles the opened and closed actions for the issue event.

For more information about the data that you can expect for each event type, see "웹후크 이벤트 및 페이로드."

const port = 3000;

This defines the port where your server should listen. 3000 matches the port that you specified for webhook forwarding. For more information, see "Forward webhooks."

Once you deploy your code to a server, you should change this to match the port where your server is listening.

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

This starts the server and tells it to listen at the specified port.

// You installed the `express` library earlier. For more information, see "[JavaScript example: Install dependencies](#javascript-example-install-dependencies)."
const express = require('express');

// This initializes a new Express application.
const app = express();

// This defines a POST route at the `/webhook` path. This path matches the path that you specified for the smee.io forwarding. For more information, see "[Forward webhooks](#forward-webhooks)."
//
// Once you deploy your code to a server and update your webhook URL, you should change this to match the path portion of the URL for your webhook.
app.post('/webhook', express.json({type: 'application/json'}), (request, response) => {

  // Respond to indicate that the delivery was successfully received.
  // Your server should respond with a 2XX response within 10 seconds of receiving a webhook delivery. If your server takes longer than that to respond, then GitHub terminates the connection and considers the delivery a failure.
  response.status(202).send('Accepted');

  // Check the `x-github-event` header to learn what event type was sent.
  const githubEvent = request.headers['x-github-event'];

  // You should add logic to handle each event type that your webhook is subscribed to.
  // For example, this code handles the `issues` and `ping` events.
  //
  // If any events have an `action` field, you should also add logic to handle each action that you are interested in.
  // For example, this code handles the `opened` and `closed` actions for the `issue` event.
  //
  // For more information about the data that you can expect for each event type, see "[AUTOTITLE](/webhooks/webhook-events-and-payloads)."
  if (githubEvent === 'issues') {
    const data = request.body;
    const action = data.action;
    if (action === 'opened') {
      console.log(`An issue was opened with this title: ${data.issue.title}`);
    } else if (action === 'closed') {
      console.log(`An issue was closed by ${data.issue.user.login}`);
    } else {
      console.log(`Unhandled action for the issue event: ${action}`);
    }
  } else if (githubEvent === 'ping') {
    console.log('GitHub sent the ping event');
  } else {
    console.log(`Unhandled event: ${githubEvent}`);
  }
});

// This defines the port where your server should listen.
// 3000 matches the port that you specified for webhook forwarding. For more information, see "[Forward webhooks](#forward-webhooks)."
//
// Once you deploy your code to a server, you should change this to match the port where your server is listening.
const port = 3000;

// This starts the server and tells it to listen at the specified port.
app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

JavaScript 예제: 코드 테스트

웹후크를 테스트하려면 컴퓨터 또는 codespace를 로컬 서버 역할로 사용할 수 있습니다. 이 단계에 문제가 있는 경우 문제 해결을 참조하세요.

  1. 웹후크를 제공하고 있는지 확인합니다. 더 이상 웹후크를 전달하지 않는 경우 웹후크 전달의 단계를 다시 따릅니다.

  2. 별도의 터미널 창에서 다음 명령을 실행하여 컴퓨터 또는 codespace에서 로컬 서버를 시작합니다. FILE_PATH를 이전 섹션의 코드가 저장된 파일의 경로로 바꿉니다.

    Shell
    node FILE_NAME
    

    Server is running on port 3000이라는 출력이 표시되어야 합니다.

  3. 웹후크를 트리거합니다. 예를 들어 issues 이벤트를 구독하는 리포지토리 웹후크를 만든 경우 리포지토리에서 이슈를 엽니다. 이전 웹후크 배달을 다시 배달할 수도 있습니다. 자세한 내용은 "웹후크 다시 제공"을(를) 참조하세요.

  4. smee.io에서 웹후크 프록시 URL로 이동합니다. 트리거하거나 다시 배달한 이벤트에 해당하는 이벤트가 표시되어야 합니다. 이는 GitHub가 웹후크 배달을 지정한 페이로드 URL로 보냈음을 나타냅니다.

  5. smee --url WEBHOOK_PROXY_URL --path /webhook --port 3000을(를) 실행한 터미널 창에 POST http://127.0.0.1:3000/webhook - 202와 같은 내용이 표시되어야 합니다. 이는 smee가 웹후크를 로컬 서버로 전달했음을 나타냅니다.

  6. node FILE_NAME을(를) 실행한 터미널 창에 전송한 이벤트에 해당하는 메시지가 표시되어야 합니다. 예를 들어 위의 코드 예를 사용하고 ping 이벤트를 다시 배달한 경우 "GitHub가 ping 이벤트를 보냈습니다"가 표시되어야 합니다.

  7. 두 터미널 창 모두에서 Ctrl+C 를 입력하여 로컬 서버를 중지하고 전달된 웹후크 수신 대기를 중지합니다.

이제 코드를 로컬로 테스트했으므로 프로덕션에서 웹후크를 사용하도록 변경할 수 있습니다. 자세한 내용은 “다음 단계”를 참조하세요. 코드를 테스트하는 데 문제가 있는 경우 "문제 해결"의 단계를 시도해 보세요.

문제 해결

테스트 단계에 설명된 예상 결과가 표시되지 않는 경우 다음을 시도해 보세요.

  • 웹후크가 웹후크 프록시 URL(Smee.io URL)을 사용하고 있는지 확인합니다. 웹후크 프록시 URL에 대한 자세한 내용은 "웹후크 프록시 URL 가져오기"를 참조하세요. 웹후크 설정에 대한 자세한 내용은 "웹후크 만들기"를 참조하세요.
  • 사용할 콘텐츠 유형을 선택할 수 있는 경우 웹후크가 JSON 콘텐츠 유형을 사용하는지 확인합니다. 웹후크 설정에 대한 자세한 내용은 "웹후크 만들기"를 참조하세요.
  • smee 클라이언트와 로컬 서버가 모두 실행 중인지 확인합니다. 해당 프로세스는 두 개의 별도 터미널 창에서 실행됩니다.
  • 서버가 smee.io가 웹후크를 전달하는 포트와 동일한 포트를 수신 대기하고 있는지 확인합니다. 이 문서의 모든 예에서는 포트 3000을 사용합니다.
  • smee.io가 웹후크를 전달하는 경로가 코드에 정의된 경로와 일치하는지 확인합니다. 이 문서의 모든 예에서는 /webhooks 경로를 사용합니다.
  • smee 클라이언트 및 로컬 서버를 실행 중인 터미널 창에서 오류 메시지를 확인합니다.
  • 웹후크 배달이 트리거되었는지 확인하려면 GitHub를 확인합니다. 자세한 내용은 "웹후크 제공 보기"을(를) 참조하세요.
  • smee.io에서 웹후크 프록시 URL을 확인합니다. 트리거하거나 다시 배달한 이벤트에 해당하는 이벤트가 표시되어야 합니다. 이는 GitHub가 웹후크 배달을 지정한 페이로드 URL로 보냈음을 나타냅니다.

다음 단계

이 문서에서는 웹후크 배달을 처리하는 코드를 작성하는 방법을 설명했습니다. 또한 컴퓨터 또는 codespace를 로컬 서버로 사용하고 smee.io를 통해 GitHub에서 로컬 서버로 웹후크 배달을 전달하여 코드를 테스트하는 방법을 보여주었습니다. 코드 테스트를 완료한 후에는 코드를 수정하고 서버에 코드를 배포할 수 있습니다.

코드 수정

이 문서에서는 웹후크 배달이 수신되면 메시지를 출력하는 기본 예를 제공했습니다. 다른 작업을 수행하도록 코드를 수정할 수도 있습니다. 예를 들어 다음을 수행하도록 코드를 수정할 수 있습니다.

  • GitHub API에 요청
  • Slack에서 메시지 보내기
  • 이벤트 로그
  • 외부 프로젝트 관리 도구 업데이트

GitHub에서 배달되었는지 확인

웹후크 배달을 처리하는 코드에서 배달을 추가로 처리하기 전에 GitHub에서 배달되었는지 확인해야 합니다. 자세한 내용은 "웹후크 제공 유효성 검사하기"을(를) 참조하세요.

서버에 코드 배포

이 문서에서는 코드를 개발하는 동안 컴퓨터 또는 codespace를 서버로 사용하는 방법을 설명했습니다. 코드를 프로덕션에 사용할 준비가 되면 코드를 전용 서버에 배포해야 합니다.

배포할 때 서버가 수신 대기하는 호스트 및 포트를 반영하도록 코드를 업데이트해야 할 수 있습니다.

웹후크 URL 업데이트

GitHub에서 웹후크 트래픽을 수신하도록 설정된 서버가 있으면 웹후크 설정에서 URL을 업데이트합니다. 새 URL의 경로 부분과 코드가 처리하는 경로가 일치하도록 업데이트해야 할 수 있습니다. 예를 들어 새 웹후크 URL이 https://example.com/github-webhooks이면 해당 예의 경로를 /webhooks에서 /github-webhooks로 변경해야 합니다.

프로덕션에서 웹후크를 전달하는 데 smee.io를 사용해서는 안 됩니다.

모범 사례 준수

웹후크를 사용하는 모범 사례를 따르는 것을 목표로 해야 합니다. 자세한 내용은 "웹후크 사용에 대한 모범 사례"을(를) 참조하세요.

추가 참고 자료