facts

페디버스(Fediverse) 서버인 sukhi-fedi가 기존에 사용하던 Bun(JavaScript 런타임) 기반의 fedify 라이브러리를 걷어내고, 모든 연합 기능을 Elixir로 직접 구현했다. 이번 교체 작업은 총 12개의 파일, 약 2,100줄의 코드를 작성하는 규모로 진행됐다.

기존에 fedify에서 사용하던 핵심 부품은 다음과 같다.

- `vocab` 클래스

- `toJsonLd`/`fromJsonLd`: 메시지 형식을 상호 변환하는 변환기

- `signRequest`/`verifyRequest`: draft-cavage 및 RFC 9421 서명 방식 지원

- LD 서명 및 document loader

- JWK(JSON Web Key, 공개키 표준 형식) 입출력

교체의 결정적 근거는 메모리 효율성과 프로세스 간 통신 비용이었다. 메모리 512~768MB 사양의 소형 서버를 목표로 한 내구 테스트 결과, Elixir 구현체는 130MB에서 안정적으로 작동했다. 반면, Bun 워커는 120MB 지점에서 OOM(Out Of Memory, 메모리 부족) 현상이 발생하며 프로세스가 종료됐다.

how-it-works

구현 과정에서 가장 핵심이 된 것은 '정답 데이터'를 활용한 검증 파이프라인이다. Ed25519 서명과 URDNA2015 정규화(문서를 일정한 순서로 정리해 동일한 해시값을 얻게 하는 규칙)는 입력값이 같으면 결과가 항상 동일하게 출력되는 결정론적 특성을 가진다. 개발자는 기존 fedify 코드를 통해 생성된 서명 결과물을 정답지로 설정하고, Elixir로 구현한 결과값이 한 글자도 틀리지 않고 일치하는지 대조하는 방식으로 무결성을 검증했다.

또한, 서로 다른 언어와 프로세스를 혼용했을 때 발생하는 '배관 작업'의 오버헤드를 제거했다. 기존 구조에서는 다음과 같은 처리 과정이 필요했다.

- 서명 검증을 위해 원본 데이터를 보존하고 전달하는 과정

- 터널링 과정에서 변경된 주소를 복원하는 작업

- DB에 저장된 키를 JWK 형식으로 포장해 다른 프로세스(Bun 워커)로 전송하는 작업

이러한 프로세스 간 경계는 데이터 변환 비용을 증가시키고 시스템 복잡도를 높이는 원인이 됐다. 이번 구현을 통해 단일 런타임 내에서 데이터 처리와 서명 검증이 가능해졌다.

한편, fedify 팀은 연합 과정의 오류를 진단하는 ActivityPub 디버깅 도구인 DrFed(https://drfed.org/)를 개발 중이다. 이 도구는 DNS, TLS, HTTP, 서명, JSON-LD 등 연합의 각 단계에서 발생하는 병목이나 오류를 짚어내며, 두 가지 서명 방식을 단계별로 추적하는 디버거 기능을 포함한다. 해당 프로젝트는 NLnet NGI0의 지원을 받으며 AGPL-3.0 라이선스로 공개될 예정이다.

implementation-impact

개발자와 실무자는 라이브러리 도입 시 '프레임워크 전체 사용 여부'와 '인프라 제약 사항'을 기준으로 판단해야 한다.

먼저, TypeScript/JavaScript 환경에서 `createFederation`, `inbox listener`, `dispatcher`와 같은 상위 프레임워크 기능을 통째로 사용한다면 fedify는 여전히 효율적인 선택지다. 실제로 Ghost의 ActivityPub 기능이나 hackers.pub, Hollo 등의 서비스가 이 구조 위에서 작동하고 있다.

하지만 다음과 같은 조건에서는 직접 구현이나 런타임 통합을 고려해야 한다.

- **극단적인 자원 제한**: 가용 메모리가 512MB 이하인 초소형 서버를 운영할 경우, 별도의 런타임(Bun 등)을 추가하는 것보다 메인 언어(Elixir 등)로 기능을 통합하는 것이 OOM 리스크를 낮추는 길이다.

- **낮은 프레임워크 의존도**: Follow에 대한 Accept 답장, 멱등성(Idempotency) 처리, authorized fetch, instance actor 등을 이미 직접 구현해 사용 중이라면, 라이브러리의 하위 부품(JSON-LD 변환, 서명 등)만 사용하는 비용보다 직접 구현하는 비용이 더 낮을 수 있다.

- **데이터 파이프라인 단순화**: 프로세스 간 데이터 전송을 위해 JWK 포장이나 주소 복원 같은 추가 로직이 계속 늘어난다면, 이는 라이브러리의 문제가 아니라 아키텍처상의 경계 문제이므로 언어 통합을 통한 최적화가 필요하다.