새로운 Python API

새로운 Python 패키지인 fluent-discourse를 소개하게 되어 기쁩니다. 이 패키지는 Discourse API를 활용하기 위해 만들어졌습니다.

Python을 통해 Discourse API를 소비하는 패키지는 이미 몇 가지 존재합니다. 그런데 왜 또 다른 패키지를 만들었을까요?

기존의 Discourse 패키지들은 문제를 해결하는 방식이 비슷합니다. API의 각 엔드포인트마다 고유한 Python 함수를 생성하는 방식이죠.

이러한 접근 방식에는 몇 가지 단점이 있습니다:

  1. API와 완전한 기능 동등성을 확보하기가 매우 어렵습니다. Discourse API는 문서화가 다소 부족한 부분이 있으며, 종종 엔드포인트를 역공학해야 합니다. 게다가 코어에 속하지 않는 API 엔드포인트를 노출하는 수많은 플러그인도 있습니다. 이는 라이브러리 사용자에게 필요한 엔드포인트를 라이브러리에 추가하기 위해 Pull Request를 보내거나, 추가 엔드포인트를 위한 맞춤형 솔루션을 직접 구축해야 하는 어려운 선택을 강요합니다.
  2. 또 다른 API를 배워야 합니다. Discourse API의 메서드와 엔드포인트를 배우는 것 외에도, Python에서 해당 호출에 대응하는 함수가 무엇인지 파악해야 합니다.
  3. 테스트해야 할 코드가 훨씬 많아지므로 100% 테스트 커버리지를 달성하기가 더 어렵습니다. 이로 인해 생성된 소프트웨어의 품질에 대한 신뢰도가 낮아집니다.

이러한 접근 방식과 달리, 저는 메서드 체이닝을 사용하여 요청을 구성하는 ‘fluent’ 인터페이스를 사용했습니다. 예를 들어 보겠습니다.
id=5인 ‘foo’ 카테고리의 최신 주제(topics)를 가져오려면 다음 엔드포인트를 사용합니다:
GET /c/foo/5.json.
이 라이브러리로 해당 요청을 보내려면 단순히 다음을 호출하면 됩니다:
client.c.foo[5].json.get()

API 엔드포인트와 Python의 대응하는 호출 사이에 의미론적 동등성이 있음을 알 수 있습니다.

이 접근 방식은

  1. 문서화되지 않은 엔드포인트, 플러그인에서 제공되는 엔드포인트, 아직 정의되지 않은 엔드포인트(미래 대비)를 포함하여 Discourse API와 완전한 기능 동등성을 기본적으로 제공합니다.
  2. Discourse API만 배우면 됩니다. 이 방식으로 어떤 API 호출이든 쉽게 변환할 수 있으므로, 그에 대응하는 함수를 찾을 필요가 없습니다.
  3. 라이브러리 전체가 약 150줄의 코드로 이루어져 있습니다. 덕분에 100% 테스트 커버리지를 달성하는 것이 매우 쉽습니다.

실제 Discourse 서버에 대한 소수의 통합 테스트를 포함하여 100% 테스트 커버리지를 달성했습니다.

이 내용이 마음에 드신다면, 이 패키지를 한번 사용해 보시길 바랍니다!

흥미로운 접근법이네요..

PHP에서 비슷한 것을 하려면 마법 메서드(magic methods)에 의존해야 하며, 그렇게 되면 IDE의 자동완성 등 장점을 잃게 됩니다. 하지만 그만한 가치가 있는 대가일 수도 있겠네요.

단순히 URL을 인자로 전달하는 client.get(url) 방식에 비해 이 접근법의 장점은 무엇인가요?

좋은 지적입니다. IDE 자동완성이 여기서 큰 도움이 되지 않을 것입니다. 이 기능이 중요하시다면 다른 라이브러리를 사용하는 것이 가치가 있을 수 있습니다.

각 메서드 체인은 새로운 클라이언트를 반환하므로, 제가 생각할 수 있는 장점 중 하나는 특정 메서드 체인을 변수에 저장하여 후속 호출에 재사용하는 것입니다. 예를 들어, 생성하려는 새 게시글의 데이터 목록이 있다고 가정해 보겠습니다.

post_data = [
  {
    "raw": "Hello World!",
    "topic_id": 123,
    ...
  },
  {
    "raw": "Tester",
    "topic_id": 501,
    ...
  },
  ...
]

“새 게시글” 메서드 체인을 저장해 두고, 목록의 각 게시글을 생성하는 데 재사용할 수 있습니다.

new_post_endpoint = client.posts.json

for post in post_data:
  new_post_endpoint.post(data=post) 

클라이언트를 재사용할 수 있는 이 기능이 유용할 수 있는 다른 시나리오를 상상해 볼 수 있을 것입니다. 그 외에도, 제안하신 것처럼 URL을 단순히 전달하는 방식보다 큰 장점은 없습니다. 이 유형의 접근 방식에 대한 근거에 대한 정보를 포함하고 있는 Universal Client 라이브러리에 대해 읽어보시길 권장합니다.

또한, 이 접근 방식의 PHP 전용 구현에 대해서는 SendGrid의 PHP API를 확인해 보세요. 저는 SendGrid의 Python API에 대해 읽었을 때 영감을 받아 이 라이브러리를 만들었습니다.