그누보드5에서 ActivityPub 설계 정보
그누보드5에서 ActivityPub 설계관련링크
본문
그누보드5에서 사용할 액티비티펍(ActivityPub)을 설계하면서 (또한 완전한 구현을 위해 현재도 진행중인) 고민들을 정리해보고자 합니다.
이 글은 ActivityPub 플러그인을 만들기 위한 고민이 정리되어 있으며, 향후 추가되는 내용도 되도록 본 글을 수정해서 첨부하는 것으로 하겠습니다.
* 그누보드 - 그누보드5용 액티비티펍(ActivityPub) 플러그인 0.1.13 > 그누보드5 플러그인 (sir.kr)
1. RESTful?
ActivityPub 프로토콜 역시 RESTful을 기반으로 합니다.
단, 우리가 지금까지 일반적으로 알고있는 RESTful과는 다른점이 있습니다. 우리가 아는 RESTful이라 함은 주로 각각의 엔드포인트(RESTful에서 'URL'와 그 의미가 같음)가 목표 시스템이 가진 세부기능과 1:1 매칭되는 방식을 취하고 있습니다.
이러한 엔드포인트에는 별도의 수식 없이 URL 자체에 그 목적과 기능이 간략하게 명시되어 있는 것이 특징입니다. (예: /api/v1/member)
sir.kr에서 찾을 수 있었던 이러한 사례를 잘 설명해주는 예시는 다음과 같습니다.
* 그누보드 - 노드를 이용해서 만들고 있는 REST API > RESTful (sir.kr) (마젠토님)
2. 기존 RESTful이 나쁜 방식은 아니지만, 피하고 싶은 문제
1번에서 언급한 것과 같은 기존 RESTful로 API를 구축하였을 때 가장 큰 문제점은 오로지 특정 어플리케이션만을 위한 구현체(라이브러리 등)가 별도로 만들어져야 한다는 점입니다. 기존의 RESTful로 만든 API라면 특정 어플리케이션(그누보드5)에서만 사용 가능한 API일 것이기 때문입니다.
이것은 개발자에게는 언어의 종속이라는 문제를 안고가게 만들 수 있고(클라이언트 예제로 제시된 언어와 환경을 중심으로만 연동 사례가 만들어질 수 있음), 운영자는 API 스펙을 보증하고 이것을 이행하기 위한 절차를 진행해야 하는 등 여러모로 골치가 아픈 사안이 될 수 있습니다.
저도 그누보드를 쓰면서 RESTful을 적용할 생각은 가지고 있었지만 결국 부정적으로 돌아서게 되는 이유도 이 때문이었습니다. 이 문제로 인해 RESTful이 필요할땐 그누보드는 애초에 제 고려대상에 올리지를 않았고 필요하다면 그누보드는 완전히 배제한 자체 프레임워크 아니면 상용 프레임워크를 이용해왔습니다.
제가 가장 이상적으로 보고있는 API 설계는, 그누보드의 존재는 물론이거니와 그누보드에서 쓰여온 필드 이름 및 접두사(g5_, mb_, bo_, wr_), 그 외 그누보드만의 배경 등에 대한 사전 지식이 아예 없는 개발자도 제시되는 스펙만 보고 구현할 수 있는 구조를 갖춘 설계라고 보고 있습니다.
sir.kr에서 이전 게시물을 쭉 둘러보던 중 이와 관련하여 공감이 가는 언급을 찾을 수 있었습니다.
"REST API는 사용하는 사람의 대부분이 예상가능하고 동의할수있는 수준의 설계원칙을 가져야하며 단순하게 json 포맷의 데이터만 반환하는걸 REST API라고 부르지는 않죠." (jihan006님)
* 그누보드 - 엔드포인트 관련해서 여쭈어봅니다. > RESTful (sir.kr)
3. API 추상화 수준에 대한 고민
ActivityPub의 경우 엔드포인트(URL)는 사실상 Inbox(들어오는 데이터)와 Outbox(나가는 데이터) 이 두가지만이 핵심입니다. (그리고 전통적인 RESTful의 조회 기능과 유사한 역할을 하는 Shares(공유할 데이터)라는 엔드포인트가 있지만, 이 부분을 그누보드5에 어떻게 잘 최적화해야 할지는 현재도 고민 중입니다.)
엔드포인트에 표기되어 있는 내용이 특정 어플리케이션에 국한되는 기능을 의미하지 않기 때문에, 주고받는 데이터를 기반으로 어떤 행위를 해야될지는 어플리케이션이 본문을 해석하여 결정하게 됩니다.
아래는 ActivityPub 플러그인을 적용한 그누보드 어플리케이션에서 주고받게 되는 메시지의 예시입니다. (날씨/환율과 같은 부가기능은 비표준이므로 언더스코어(_)로 표기하였음)
{ "@context": "https://www.w3.org/ns/activitystreams", "type": "Create", "id": "http://example.org/bbs/board.php?bo_table=apstreams&wr_id=235", "to": ["https://www.w3.org/ns/activitystreams#Public", "http://example.org/?route=activitypub.user&mb_id=admin"], "actor": "http://example.org/?route=activitypub.user&mb_id=admin", "object": { "type": "Note", "generator": "GNUBOARD5 ActivityPub Plugin (INSTANCE_ID: 4d6076784cbd864ade7c746690d37051, INSTANCE_VERSION: 0.1.11-dev)", "id": "http://example.org/bbs/board.php?bo_table=free&wr_id=1", "attributedTo": "http://example.org/?route=activitypub.user&mb_id=admin", "content": "안녕하세요 @*** 개인정보보호를 위한 이메일주소 노출방지 ***", "icon": "https://www.gravatar.com/avatar/bdbd5eb70305f1eaaa0340687758676a", "location": { "name": "xxx.xxx.xxx.xxx, 서울특별시 금천구 가산동 (Korea Telecom), Seoul, Seoul-teukbyeolsi, Korea (Republic of), KR, 06030, +09:00", "type": "Place", "longitude": 126.8917326, "latitude": 37.4769094, "units": "m", "_weather": { "dt": 1657163472, "sunrise": 1657138663, "sunset": 1657191385, "temp": 305.42, "feels_like": 309.65, "pressure": 1005, "humidity": 56, "dew_point": 295.52, "uvi": 8.53, "clouds": 100, "visibility": 10000, "wind_speed": 5.72, "wind_deg": 186, "wind_gust": 10.14, "weather": [{ "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04d" }] }, "_exchange": { "KRW": { "AED": 355.94, "AUD": 887.07, "BHD": 3467.72, "BND": 930.73, "CAD": 1003.15, "CHF": 1346.86, "CNH": 194.76, "DKK": 178.9, "EUR": 1331.33, "GBP": 1558.81, "HKD": 166.61, "IDR(100)": 8.72, "JPY(100)": 960.93, "KRW": 0, "KWD": 4253.09, "MYR": 295.49, "NOK": 128.98, "NZD": 804.44, "SAR": 348.27, "SEK": 124.02, "SGD": 930.73, "THB": 36.12, "USD": 1307.4 } } } }, "published": "2022-07-07T03:11:12Z", "updated": "2022-07-07T03:11:12Z" } |
위에 제시된 예시 본문에서 특히 [id] 필드 부분을 강조한 이유는 그누보드에서 ActivityPub이 적용되었을 때 가장 큰 장점으로 작용하리라 예상되는 부분이기 때문입니다.
실제 어떤 액션을 취해야할 대상(타겟)을 지칭하기 위해 기존의 RESTful처럼 별도의 엔드포인트를 생성해서 쓰는 것이 아니라, 그냥 컴퓨터 화면 상에서 보고 있는 글의 URL과 동일하게 지정하면 되기 때문입니다.
만약, sir.kr의 g5_plugin라는 게시판의 10993번째 게시물에 댓글을 달고 싶다면, 대상(타겟)을 지칭하기 위해 아래와 같이 쓰면 됩니다.
* 원본 URL: https://sir.kr/bbs/board.php?bo_table=g5_plugin&wr_id=10993
* 액션을 수행할 대상(타겟)을 지정할 때:
{ "id": "https://sir.kr/bbs/board.php?bo_table=g5_plugin&wr_id=10993" }
기본적으로 엔드포인트의 무리한 변환을 시도할 필요가 없어지므로, 엔드포인트 변환에서 비롯될 수 있는 수많은 문제들이 해결될 수 있습니다.
3.1. HTTP 메소드와 웹호스팅 문제
흔히 외국 솔루션에서 언급되는 RESTful이라고 하면 HTTP 메소드의 GET, POST 말고도 PUT, DELETE 등의 부가적인 메소드를 지원하도록 설계됩니다.
하지만, 웹호스팅(리눅스 또는 윈도우를 사용하는 서버 위에서 계정을 공유하여 사용하는 환경)에서 PHP 솔루션을 사용하는 경우가 대부분이고 이 경우 GET, POST만이 허용되는 경우가 매우 많습니다. 특히, 국내 웹호스팅 사정은 더욱 그렇습니다.
ActivityPub의 경우 GET, POST 메소드만을 사용하고, RESTful의 PUT, DELETE 등에 해당할 수 있는 액션은 본문의 [type] 필드로 지정하기 때문에 이 문제에서 자유롭습니다.
4. 컨텐츠 접근 수준에 대한 고려
곧 Shares(공유할 데이터) 엔드포인트에 대한 구체적인 설계 및 구현에 들어갈 계획입니다. 이 과정에서 그누보드에서 정의하고 있는 게시판 권한 및 포인트라는 개념을 어떻게 잘 해석해서 컨텐츠 접근 수준에 녹여내야 잘했다고 소문날까 고민 중입니다. ㅋㅋ
5. 그누보드 기반의 어플리케이션이라 할지라도 외국 수준과 견주었을 때 현대적이라고 인정받고 싶음
훌륭한 외국 솔루션들 물론 많지만 우리나라 개발 환경에서 요구되는 특수성에 만족하는 어플리케이션은 찾기 쉽지 않습니다.
이러한 우리나라만의 특수성이라는걸 여기서 길게 설명할 순 없지만, 그 일부분이라도 영어로 해석해서 Reddit 같은 커뮤니티에 질의하였을 때 국제적으로 통용되는 규칙들과 비교해서 확실히 이상한 점이 많다는 평이 대부분이었습니다. 외국에는 우리나라에서 나올만한 요건을 만족시켜 줄 곳이 없다는거죠.
현재로서는 그런 특수성에도 대응할 수 있는 솔루션이 그누보드말고 더 있나 싶습니다. 특수성을 유지하면서도 국제적인 기준으로 봐도 충분히 현대적인 개발을 하고 있음을 보여주고자 하는 의도도 있습니다.
향후 업데이트되는 내용에 따라 이 글에도 내용을 더 추가하겠습니다. ㅎㅎ
3
댓글 1개
늘 개발자 분들에게 감사한 마음입니다. ^^