ActivityPub 구현이 어려운 이유와, 꼭 그럴 필요가 없는 이유
요약
ActivityPub 프로토콜 구현 시 발생하는 JSON-LD 처리의 어려움과 설계적 대안을 다룹니다. 서비스를 MTA와 MUA 모델로 분리하여 복잡성을 관리하는 방식과 구현 시 고려해야 할 기술적 도전 과제들을 제안합니다.
핵심 포인트
- JSON-LD의 표준 표현과 직렬화 문제로 인한 구현 난이도 상승
- 서비스를 MTA(메시지 전송)와 MUA(애플리케이션)로 분리하는 아키텍처 제안
- JSON Schema 및 OpenAPI 스키마 간의 상호운용성 문제 지적
- 상호운용성 버그 관리를 위한 자동화된 문서화의 필요성
ActivityPub 프로젝트에 서로의 포크가 많은 이유가 이거임: 직접 전부 구현하는 것보다 남의 접근법을 파악하는 편이 더 쉬움
글쓴이가 제안하는 것도 실제로 흔히 보이는 Misskey나 Pleroma 포크와 크게 다르지 않아 보임. 라이브러리에도 나름의 관점과 접근법이 있고 제어권을 많이 주지는 않는 듯함. 그래도 전체 서버를 포크할 때처럼 UI까지 강제하지는 않는다는 장점은 있음
AP를 구현 중인 입장에서 가장 어려운 부분은 JSON-LD를 제대로 쓰는 좋은 방법이 없다는 것임. 객체를 표준 표현으로 쉽게 변환할 수 있다면 상호작용은 자연스럽게 따라올 텐데, 진짜 연결 문서처럼 쓰기에는 너무 비효율적이고, 원시 JSON 문서처럼 쓰면 수많은 예외 케이스에 죽어나감. 지금까지는 두 번째 접근을 택했다가 죽었음
특히 서명을 생각하면 “객체의 표준 표현” 문제는 더 중요함. 예전 XML 정규화는 바로 이 서명 문제, 즉 수신자의 바이트 직렬화가 송신자의 것과 일치하도록 보장하기 위해 있었음
JSON-LD 세계와 완전히 같은 문제는 아니지만, 완전히 무관하지도 않음
다만 JSON 인접 기술 상당수가 비슷한 문제를 겪는다고 봄. 같은 논리적 스키마를 표현하는 JSON Schema 방식이 너무 많고, 그 때문에 JSON Schema 주변 기술과 상호작용하는 일이 우스울 정도로 끔찍해짐. 특히 OpenAPI 스키마는 비슷하지만 같지는 않은 공포물이고, 스키마 초안 버전 수까지 고려하지 않아도 이미 충분히 나쁨
AP 서버 구현을 생각해 왔지만 아직 시작하지는 않았으니 크게 걸러 들어야 함. 도움이 될 수 있는 한 가지는 애플리케이션을 더 작은 서비스들로 나누고, 액터 모델에 더 기대어 이를 “통합된” 인터페이스처럼 보이게 하는 방식임. 예를 들어 이메일 서버의 MTA와 MUA 분리를 배울 수 있음
AP의 “MTA” 서비스는 발신함에서 메시지를 보내고 수신함으로 메시지를 받는 일을 맡음. JSON-LD 문서는 이 서비스 입장에서는 거의 덩어리 데이터에 가까움. 송신자와 수신자를 알아내기 위한 약간의 파싱은 필요하지만 그 이상은 많지 않음. 저장소도 파일 기반일 수 있고, 기억이 맞다면 go-ap가 그런 식을 씀
AP의 “MUA”는 실제 애플리케이션임. JSON-LD 의미를 이해해야 하는 쪽임. PostgreSQL 같은 것을 써서 문서를 jsonb로 저장하고, 생성 컬럼과 뷰로 SQL 친화적인 형태를 제공할 수 있을 듯함. 그러면 객체 타입에 따라 문서를 가장 적절하게 표현하는 방식을 정할 수 있음
또 다른 예로 검색 서비스도 액터로 모델링해서 결과를 임시 발신함으로 반환하게 만들 수 있음
여러 구현체의 특이한 동작과 완화책을 정리한 매우 귀중한 목록임
아쉽게도 GoActivityPub에는 그중 절반도 아직 구현하지 못했음
글이 처음에는 기술적인 내용으로 시작해서 고마웠는데, 중간부터 자기 프레임워크 홍보로 방향을 튼 듯해서 읽는 맛이 떨어졌음
TypeScript를 쓰는 일부 세계에서는 이런 구현상 특이점을 다시 발견하지 않아도 될 수 있어 다행임. 하지만 머릿속 모델로는 “이 조건과 상황에서는 이런 결과가 나오고, 이런 수정이 필요하다”는 기록이 있다면 TypeScript가 아닌 상황, 예컨대 형제 프로젝트인 GoActivityPub 작성자도 그 고생의 결과를 얻을 수 있음. 여기서는 그런 것들을 몇 가지 다루긴 했지만, 글은 특정 시점의 스냅샷이고 프로젝트는 시간이 지나며 모든 상호운용성 버그를 축적하려는 것처럼 보임
현재 대안은 내가 보기엔 사람이 쓴 것이 아닌 커밋 메시지를 전부 읽어가며, Fedify 자체 버그와 상호운용성 버그를 구분하는 것뿐임
특히 저장소가 AI에 “올인”한 것처럼 보이는데도 그런 장부 정리를 하지 않는 건 아이러니함. LLM에 대해 들어온 홍보는 반복 잡무를 자동화한다는 것이었음. 그렇다면 Claude가 GitHub 이슈를 만들거나, 더 좋게는 저장소 안의 .md 파일로 관찰된 결과와 Fedify가 이를 어떻게 고치는지 문서화하게 하면 됨. 자체 디버거도 있고 무엇을 뜻하는지 모를 “모범 사례”도 있으니 딱 맞는 일일 것임
정말로 사소한 문제를 과장해서 ActivityPub의 실패처럼 제시함. 예를 들어 팔로워 5천 명이면 게시물 하나가 수천 건의 HTTP 전달이 되고, 이를 요청 처리기 안에서 직접 하면 게시 버튼 응답에 30초가 걸리거나 서버가 쓰러지니 큐를 쓰라는 식임
왜 제3자 서비스로 보내는 요청을 인라인으로 수행함? 이런 건 웹 애플리케이션 기본기임. 제3자 서비스와 통신해야 하면 백그라운드 작업으로 보내야 함. 요청에 응답하는 데 필요 없는 정보라면 백그라운드 작업으로 보내야 함. 요청 처리기 안에서 이런 요청을 하다가 생기는 문제는 갈퀴를 밟아 얼굴을 맞는 수준의 자초한 문제지, ActivityPub과는 상관없음
전달이 실패하면 재시도해야 하고, 일정은 어떻게 할지, 지수 백오프를 쓸지, 몇 번 할지, 500 Internal Server Error와 410 Gone을 같은 실패로 볼지 같은 것도 그냥 일반적인 웹 애플리케이션 개발 문제임. 작업 큐에서 제3자 서비스에 요청할 때 생기는 문제고 ActivityPub과 무관함. 대부분의 웹 프레임워크에는 합리적인 기본값이 있음. 어떤 오류가 났는지에 따라 재시도 여부를 정해야 하는 지점에서만 판단이 필요함. 410을 재시도하는 건 낭비지만 급히 해결해야 할 문제는 아님. 작업 큐의 메모리 압박은 늘리겠지만 몇 시간 안에 애플리케이션을 쓰러뜨릴 가능성은 낮음
“거절되는지 보고, 다른 방식으로 다시 서명하고, 서버별로 어떤 방식이 통했는지 기억하라”니 대체 뭘 읽고 있는 건가 싶음. 이래서 Mastodon 개발이 느린 건가?
“게시물 하나가 수천 건의 HTTP 전달”이라니, 네트워크 시스템 프로그래밍과 큐잉에 뛰어난 언어로 유명한 Ruby에서 말이지
믿기 어렵고, 이걸 라이브러리로 감싸둔 건 좋지만 그래도 좀 그렇다
Java로 ActivityPub을 구현해 본 뒤, 이런 서버 간 프로토콜은 그냥 git 위에 만드는 편이 낫다는 결론에 도달했음
복잡성의 상당 부분이 git이 이미 더 잘 해결하는 문제를 다시 풀기 위해 존재함. 이를 git 저장소 안의 JSON 문서로 모델링하면 페이지네이션을 다루지 않아도 됨. 프로토콜이 이미 없는 데이터만 보내도록 보장하고, 커밋 서명도 얻고, 이벤트 순서 보장도 얻고, 이 글에서 언급한 문제도 해결되며, 이력도 공짜로 생김. 이런 프로토콜에는 버그 많고 느린 git 절반 구현이 들어 있다는 식의 Greenspun의 열 번째 법칙 비슷한 말을 만들 수 있을 듯함
git은 부모 커밋에 이력이 의존하므로 훌륭한 선택은 아님. 다만 비슷한 협상 전략으로 동작하는 머클 트리 가십 프로토콜은 잘 맞을 수 있음
이 글은 AI가 만든 저품질 글처럼 읽힘
더 구체적으로는 왜 이야기 형식으로 썼는지 모르겠음. 여기서 전달하는 사실들은 훨씬 더 간결하고 덜 편향적으로 쓸 수 있었고, 서사도 설득력이 없음. 특히 AI 특유의 표현들이 많아서 그렇다
즐겁게 읽히지 않았음. 그래도 문제들을 짚어준 건 고맙고, 다른 방식으로 그 문제들을 읽고 고칠 수 있기를 바람
AI 자동 생성 콘텐츠
본 콘텐츠는 GeekNews의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기