본문 바로가기
SW 개발/REST API

라이딩 앱 Strava API 사용해보기: Webhook 구현

by Kibua20 2020. 12. 26.

Webhook 이란

Webhook은 'user-defined http callback'으로 서버에서 데이터 변경이 발생한 경우 클라이언트로 변경 사항을 알려주는 메커니즘입니다. 서버에서 데이터 변경이 있을 경우 사용자가 등록한 Trigger URL(callback url)을 호출하는 방식을 사용합니다.   

 

Webhook의 유용한 점은 서버에서 데이터 변경 시점을 알려주시기 때문에 클라이언트에서 주기적으로 서버 데이터를 체크하는 Polling을 사용하지 않아 데이터 Traffic을 줄일 수 있고, Polling으로 인한 서버 부하를 줄일 수 있습니다.

Webhook 이란 (출처: 위키 백과)

A webhook in web development is a method of augmenting or altering the behavior of a web page or web application with custom callbacks. These callbacks may be maintained, modified, and managed by third-party users and developers who may not necessarily be affiliated with the originating website or application. The term "webhook" was coined by Jeff Lindsay in 2007 from the computer programming term hook.

The format is usually JSON. The request is done as an HTTP POST request. Webhooks are "user-defined HTTP callbacks". They are usually triggered by some event, such as pushing code to a repository or a comment being posted to a blog. When that event occurs, the source site makes an HTTP request to the URL configured for the webhook.

Users can configure them to cause events on one site to invoke behavior on another. Common uses are to trigger builds with continuous integration systems or to notify bug tracking systems. Because webhooks use HTTP, they can be integrated into web services without adding new infrastructure.

 

Strava API 사용 시 Webhook 이 없다면 사용자의 데이터를 언제 동기화할지 모르기 때문에 클라이언트에서 주기적으로 서버에 데이터를 얻어와서 변경 여부를 체크해야 합니다.  Webhook을 사용하는 경우 서버에 데이터가 업데이트되는 시점을 Webhook으로 등록한 URL로 알려주기 때문에 필요한 시점에서만  Strava API 호출하여 데이터를 동기화할 수 있습니다.  

 

Strava Webhook Event Data

Strava API에서 Webhook 가이드는 링크에서 확인이 가능합니다.  Strava Webhook API에서는 athlete와 activitiy object의  Title, Type과 같은 데이터 생성, 삭제, 갱신된 경우 클라이언트로 이벤트를 전달합니다.  API scope 중 activity:read_all에 대한 Permission을 가지고 있어야 사용 가능합니다.  

클라이언트 받는 Event Data type

Sample Event Data:

{
    "aspect_type": "update",
    "event_time": 1516126040,
    "object_id": 1360128428,
    "object_type": "activity",
    "owner_id": 134815,
    "subscription_id": 120475,
    "updates": {
        "title": "Messy"
    }
}

 

 

Strava Webhook  API 등록하기

Strava에서 Webhook URL 등록은 하나의 앱에서는 1개만 허용됩니다.  Webhook의 callbac url로 등록하는 URL 은 외부 인터넷에서 접속 가능해야 하고, URL 처리를 위해서는 Web server가 설치되어 있어야 합니다. 이러한 테스트 용도로 ngrok이나 localtunnel을 활용할 수 있으며 이전 포스팅을 참고하세요. Web server는 flask를 활용했습니다.

 

Strava Webhook API 구현의 과정을 요약하면 아래와 같습니다.

  1. Create Subscription
  2. Check Subscripttion Validation
  3. 서버의 사용자 데이터 업데이트 시 Event 받기

Strava API 에서 Web Hook 구현

 

1. Create a subscrtiption

Webhook Event를 받을 URL을 등록하는 과정입니다. Client ID와 Client Secrete은 App 등록 시 할당받은 값으로 적용하면 되고, callback_url은 Webhook 이벤트를 받을 서버 URL로 적용해야 합니다.  Strava 서버에서 callback_url로 변경된 데이터를 JSON으로 POST method로 호출하고, Webhook 서버에서는 callback_url에 대한 적절한 응답을 해야 합니다. Webhook 서버에서 응답이 없거나 잘못된 경우 에러가 발생할 수 있습니다.  마지막 인자인 verify token은 임의 string 값으로 넣을 수 있고, webhook validation 시 활용할 수 있는 값입니다.  

 

 

POST https://www.strava.com/api/v3/push_subscriptions 

curl 명령어 - Webhook URL 등록

$ curl -X POST https://www.strava.com/api/v3/push_subscriptions \

  -d client_id=123456 \

  -d client_secret=abcdedabcdedfi \

  -d 'callback_url=http://a-valid.com/url' \

  -d 'verify_token=STRAVA'

 

API 요청

https://www.strava.com/api/v3/push_subscriptions?client_id=123456&client_secret=abcdedabcdedfi&callback_url=http://193.***.***.***/webhook&verify_token=STRAVA

 

Postman 명령어

 

2. Check Subscripttion Validation

Webhook 서버에서 Response 없이 https://www.strava.com/api/v3/push_subscriptions API 만 호출하는 경우 Strava 서버에서 아래와 같은 "challenge response malformed" 에러를 응답합니다. 

 

Webhook 에러 메시지

{

"message""Bad Request",

"errors": [

   {

         "resource""PushSubscription",

         "field""challenge response",

         "code": "challenge response malformed"

   }

 ]

}

 

Strava 서버에서는 Webhook 서버의 callback_url으로 {hub.mod, hub.chanllege, hub.verify_token}을  GET로 JSON 데이터를 전달하고 Webhook 서버에서 callback_url에 대한 응답(response)으로 {hub.chanllge} 값을 전달해야 합니다.

Subscripttion Validation

Strava 서버에서 Webhook callback url 호출

$ GET https://mycallbackurl.com?hub.verify_token=STRAVA&hub.challenge=15f7d1a91c1f40f8a748fd134752feb3&hub.mode=subscribe

 

Webhook 서버에서 callback url 처리 

Webhook 서버에서 callback_url 처리하는 python flask code는 아래와 같습니다.

Webhook 서버에서 callback_url 처리하는 python flask code

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

from flask import Flask, send_file, jsonify, request, Response

app = Flask(__name__)

@app.route('/webhook', methods=['GET', 'POST'])
def getwebhook():
    print (request, file=sys.stderr)
    #print (request.args.get, file=sys.stderr)

    if request.method == 'GET':
        try:
            #subscribe
            #request.args.get
            mode = request.args.get('hub.mode')

            #TODO: Check verify_token
            verify = request.args.get('hub.verify_token')

            if (mode == 'subscribe'):
                challenge = request.args.get('hub.challenge')
                res= {'hub.challenge': challenge}
                return jsonify(res)
            else:
                return 'Else in get !!'
        except:
            return 'Except !!'
    elif request.method == 'POST':
        #TODO: Parse request data
        print (request.get_json(), file=sys.stderr)
        return jsonify (request.get_json())
    else:
        return 'Never reach'

        
        
if __name__ == "__main__":
    #sudo python3 server.py
    app.run(host='0.0.0.0', port='80', debug=True)

 

Strava  서버에서 subcription ID 응답

Subscription에 대한 Validation이 정상적으로 진행되었다면 그 결과로 Strava 서버에서는 Subscription ID를  최종적으로 응답을 해줍니다.

{

    "id": 1

}

 

3. 서버의 사용자 데이터 업데이트 시 Event 받기

Webhook URL이 정상적으로 등록되었다면, Strava 서버에서 사용자 데이터가 변경된 경우 아래와 같이 callback_url을 Event data를 JSON형태로 받을 수 있습니다.  사용자 데이터가 업데이트되고 즉시 callback이 호출되는 것이 아니고 대략 10분 정도 후에 callback_url을 호출하며, 변경된 object의 ID를 알려줍니다. 

 

Web hook Event data

{

  'aspect_type': 'update',

  'event_time': 1607074771,

  'object_id': 4427581030,

   'object_type': 'activity',

   'owner_id': 73248226,

   'subscription_id': 175261,

   'updates': {'type': 'AlpineSki'}

}

 

 

4. View a subscrption

Strava 서버에 등록된 subscription을 https://www.strava.com/api/v3/push_subscriptions을 사용해서 확인할 수 있습니다.

 

curl

$ curl -G https://www.strava.com/api/v3/push_subscriptions \

     -d client_id=5 \

     -d client_secret=7b2946535949ae70f015d696d8ac602830ece4123

 

Postman

View a subscrption

 

5. Delete subscription

Strava 서버에 등록된  callback_url을 아래 명령어로 삭제할 수 있습니다.

 

curl

$ curl -X DELETE https://www.strava.com/api/v3/push_subscriptions/{subscription_id} \

   -d client_id=5 \

   -d client_secret=7b2946535949ae70f015d696d8ac602830ece412

 

Postman

Delete subscription

 

※ 참고 링크

Authentication: https://developers.strava.com/docs/authentication

Webhookshttps://developers.strava.com/docs/webhooks

API docs: https://developers.strava.com/docs/reference

Basic info: https://developers.strava.com/docs

Android에서 webhook 구현:  stackoverflow.com/questions/46336811/how-do-i-implement-a-webhook-in-android

 

관련 글:

[모바일 SW 개발/REST API] - 라이딩 앱 STRAVA API 연동 방법 (Sample code)

[개발환경/Google Cloud Platform] - 회사에서 방화벽으로 막혀 있는 사이트 우회 방법: SSH tunneling 과 Socks5 활용

[모바일 SW 개발/REST API] - 외부 망에서 Localhost를 접속하기: ngrok (일부 무료)

[모바일 SW 개발/REST API] - 외부 망에서 Localhost를 접속하기: localtunnel (무료, domain제공)

[모바일 SW 개발/REST API] - 자주 사용하는 curl 명령어 옵션과 예제

[모바일 SW 개발/REST API] - 무료 REST API 테스트 프로그램: Postman (설치, 활용법)

[모바일 SW 개발/REST API] - 공공 데이터 Open API 사용법: 코로나 확진자 현황 API (sample code)

[개발환경/우분투] - Docker 개념과 명령어 사용 방법 및 예제

[개발환경/Google Cloud Platform] - IP Address CIDR 표현법과 사용 예

[개발환경/Oracle Cloud] - Oracle Cloud 고정 IP (공인 IP) 할당하기

[개발환경/Oracle Cloud] - 오라클 클라우드 '평생' 무료 VM 만들기 (Google Cloud 무료 조건 비교)

[개발환경/Web Server] - 우분투 20.04에서 Flask를 서비스 등록: 부팅 시 자동 실행

[모바일 SW 개발/Python] - Python: JSON 개념과 json 모듈 사용법

[개발환경/Web Server] - Python: Web Framework Flask 사용하기

[개발환경/Web Server] - Web 서버 GET/POST CGI 사용법 (QUERY_STRING / CONTENT_LENGTH)

[개발환경/Web Server] - 우분투 20.04에서 lighttpd Web Server 설치 (Embedded용으로 활용 가능)

[모바일 SW 개발/REST API] - Service Account(JWT)을 활용한 Google Calendar API 사용

[모바일 SW 개발/REST API] - JWT(JSON Web Token) Encoding 방법 (Python sample code)

[모바일 SW 개발/REST API] - Google gmail API 사용 방법 (3) - Sample code

[모바일 SW 개발/Python] - Python JSON 사용 시 TypeError: Object of type bytes is not JSON serializable




댓글