Lee Dogeon

@moreal@hackers.pub · 76 following · 69 followers

어느 한 개발자입니다.

GitHub
@moreal
4
3

5월 24일(土) 한국 연합우주 개발자 모임(FediDev KR)에서 두 번째 스프린트 모임을 개최합니다! 장소는 뚝섬역 5번 출구쪽에 위치한 튜링의 사과(@TuringAppleDev튜링의 사과)입니다.

참고로 스프린트 모임이란 함께 모여서 오픈 소스 코딩을 하는 자리인데, 한국 연합우주 개발자 모임의 스프린트에서는 새로운 연합우주 서비스나 앱을 개발하거나, 번역이나 문서에 기여하는 등 연합우주와 관련된 다양한 오픈 소스 활동을 모여서 함께 합니다. 지난 스프린트 모임의 기록을 스프린트 블로그(@sprints.fedidev.kr한국 페디버스 개발자 모임)에서 살펴보실 수 있습니다.

저는 그날 Fedify, Hollo, Hackers' Pub에 기여하시고자 하는 분들을 옆에서 도와드릴 예정입니다. Fedify, Hollo, Hackers' Pub에 기여해보고 싶었던 분들이 계시다면 모임에 참가하여 저와 함께 스프린트를 해보는 것도 좋을 것 같습니다.

이번 모임에 관심이 있으신 분은 행사 신청 페이지를 참고하시기 바랍니다.

7
1
3
1
1

안녕하세요, 업으로 프로그래밍을 하고 있는 컴퓨터 학부생 김무훈입니다.
현재 3년차 웹 프론트엔드 개발자로서, 다가오는 7월부터 함께할 정규직 포지션을 적극적으로 찾고 있습니다.

최근 학과 사무실에서 졸업 요건을 확인한 결과, 전공 필수 한 과목전공 선택 2학점(총 5학점)이 남아있음을 확인했습니다.
본래는 다음 2학기까지 수료 후 내년 2월에 졸업할 예정이었으나, 교수진과 상의한 결과 취업 및 재직이 확정된다면 수업 이수 방식을 보다 유연하게 결정할 수 있다는 긍정적인 답변을 받아 적극적으로 조기 취업을 추진하게 되었습니다.

이는 전공 필수 과목의 경우에만 해당이 되는 문제이고, 전공 선택 2학점의 경우 앞으로의 여름 학기 현장 실습 또는 다음 학기에 개설되는 하나의 원격 강의로 대체하여 문제가 없는 상태입니다.

지금까지의 업무 경험과 프로젝트는 아래의 포트폴리오에서 확인하실 수 있습니다.
📌 경력기술서 겸 포트폴리오 페이지: https://www.frontend.moe/portfolio/

좋은 인연을 찾을 수 있도록, 많은 관심과 연락 부탁드립니다!

8
2
1
1

현재 Hackers' Pub은 Fresh 2.0 알파 버전을 사용하고 있는데, Fresh 자체의 한계점도 많이 느꼈고 무엇보다 최근 몇 달 사이에 정식 릴리스를 향한 진전이 보이지 않기에 GraphQL 준비가 끝나면 프런트엔드를 SolidStart로 점진적으로 옮겨가고자 한다.

3

Lee Dogeon shared the below article:

Hackers' Pub 업데이트: LLM 기반의 게시글 번역 기능

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

LLM 기반의 게시글 번역 기능이 추가되었습니다. 우선, 자신이 쓴 게시글이 LLM을 이용해 번역되는 것을 허용하려면, 게시글 공개 설정에서 “LLM 기반 자동 번역 허용” 옵션을 켜 주셔야 합니다. 기존 게시글은 모두 이 옵션이 꺼져 있습니다만, 새로 쓰는 게시글의 경우 기본적으로 켜져 있습니다.

한국어판 게시글 공개 설정 페이지에 추가된 “LLM 기반 자동 번역 허용” 옵션
한국어판 게시글 공개 설정 페이지에 추가된 “LLM 기반 자동 번역 허용” 옵션
영어판 게시물 공개 설정 페이지에 추가된 “Allow LLM-powered automatic translation” 옵션
영어판 게시물 공개 설정 페이지에 추가된 “Allow LLM-powered automatic translation” 옵션

위와 같이 옵션을 켜 준 게시글은 위쪽에 다음과 같이 “다른 언어로 읽기” 메뉴가 표시되게 됩니다. 이 메뉴에 나오는 언어 목록은 언어 설정에서 정할 수 있습니다.

게시글 첫 부분에 표시되는 “다른 언어로 읽기” 메뉴 (한국어판)
게시글 첫 부분에 표시되는 “다른 언어로 읽기” 메뉴 (한국어판)
게시글 첫 부분에 표시되는 “Read in other languages” 메뉴 (영어판)
게시글 첫 부분에 표시되는 “Read in other languages” 메뉴 (영어판)

이 중에서 이미 번역이 완료된 언어는 바로 표시되지만, 아직 번역이 완료되지 않은 언어의 경우, 아래와 같이 기다리라는 메시지가 뜨게 됩니다. 게시글의 분량에 따라 번역 시간은 차이가 나지만, 짧으면 30초에서 길면 5분 정도 걸립니다.

게시글이 번역중이라는 메시지 (한국어판): “이 게시글은 영어에서 한국어로 번역중입니다. 번역이 완료될 때까지 기다려 주세요.”
게시글이 번역중이라는 메시지 (한국어판): “이 게시글은 영어에서 한국어로 번역중입니다. 번역이 완료될 때까지 기다려 주세요.”
게시글이 번역중이라는 메시지 (영어판): “This article is being translated from Korean to English. Please wait until the translation is complete.”
게시글이 번역중이라는 메시지 (영어판): “This article is being translated from Korean to English. Please wait until the translation is complete.”

번역이 완료되면, 아래와 같이 메시지가 바뀝니다.

게시글의 번역본 상단에 뜨는 메시지 (한국어판): “이 게시글은 영어에서 한국어로 번역되었습니다.”
게시글의 번역본 상단에 뜨는 메시지 (한국어판): “이 게시글은 영어에서 한국어로 번역되었습니다.”
게시글의 번역본 상단에 뜨는 메시지 (영어판): “This article has been translated from Korean to English.”
게시글의 번역본 상단에 뜨는 메시지 (영어판): “This article has been translated from Korean to English.”

번역 기능은 제가 Hackers' Pub을 맨 처음 구상할 때부터 핵심 기능으로 고려하고 있던 것이었습니다. 소프트웨어 프로그래머로서 일정 수준 이상 성장하기 위해서는 반드시 영어를 배워야만 하는 불합리함이나 그리고 일본어나 중국어 등 영어가 아닌 언어로 쓰인 다양한 자료에 대부분의 외국인은 접근하지 못한다는 아쉬움을 오래 전부터 느꼈기 때문입니다. 다행히 얼마 전부터 LLM의 번역 품질이 아주 좋아졌고, 이를 활용하여 꽤 괜찮은 품질의 번역 기능을 Hackers' Pub 같은 작은 웹사이트에서도 구현할 수 있게 되었네요.

참고로 현재 번역에 쓰이는 모델은 Claude Sonnet 3.7입니다. 저렴하다고는 할 수 없는 모델인데요. 시범적으로 운영해 보고, 비용이 너무 부담된다고 여겨지면 Gemini 2.5 Flash 같은 다른 모델로 전환하는 것도 고려하고 있습니다.

아무튼, 모처럼 추가한 번역 기능이니 많은 분들이 유용함을 누리셨으면 좋겠습니다.

아래는 제가 샘플로 미리 만들어 둔 번역본들입니다:

  • Ditch the DIY Drama: Why Use Fedify Instead of Building ActivityPub from Scratch? (영어) → 〈DIY 드라마는 그만: 왜 ActivityPub을 처음부터 구축하는 대신 Fedify를 사용해야 할까요?〉 (한국어)
  • 〈애플리케이션 개발 측면에서 본 Drizzle ORM 대 Kysely 비교〉 (한국어) → 「アプリケーション開発の観点から見たDrizzle ORMとKyselyの比較」 (일본어)
  • 〈deno-task-hooks: Git 훅을 Deno 태스크로 쉽게 관리하기〉 (한국어) → deno-task-hooks: Easily Manage Git Hooks as Deno Tasks (영어)
  • Browser-Native Translation and Language Detection APIs Coming Soon (영어) → 〈브라우저 네이티브 번역 및 언어 감지 API 곧 출시 예정〉 (한국어)
Read more →
6

Signal같은건데 incremental update도 되고 GC도 가능한 무언가를 만들려고 했더니 이런 정의가 나왔다. 혹시 비슷한거 알고 계신분 있나요?

type Dynamic<Value, Delta> = {
	read(): Value;
	disconnect(): void;

	updated: Observable<Delta>;

	fork(): Dynamic<Value, Delta>;
};
3

.github/copilot-instructions.md, .cursorrules, .windsurfrules, CLAUDE.md… 이것 말고도 많이 있을텐데, 어차피 들어가야 하는 내용은 다 거기서 거기. 지금은 한 파일에 적고 심볼릭 링크로 같은 곳을 바라보게 하고 있지만, .editorconfig처럼 그냥 어떤 식으로든 표준화가 되었으면 좋겠다.

1

웹 앱들이 카메라, 위치 등 권한을 어떤 방식으로 요청하고 있는지, 사용자가 이에 어떻게 반응하는지에 관한 연구. 많은 웹 사이트가 사용자에게 아무런 맥락없이 권한을 요청한다. 기본적으로 사용자에게 권한이 왜 필요한지 설명하면 허용률이 높아졌고, 긍정적인 톤으로 권한을 요청하면 허용률이 18% 증가한다. 텍스트만 보여주기 보다는 UI 요소가 있을 때 허용률이 더 높았는데, 오버레이(+41%) 또는 전체화면(+33%)으로 권한을 요청하면 허용률이 늘지만 사용자의 불만족도 높아졌다. programs.sigchi.org/chi/2025/p

Permission Rationales in the Web Ecosystem: An Exploration of Rationale Text and Design Patterns
3
10

백엔드는 모르겠고, 프론트엔드 대상으로는 많이들 권장하는 책이던데 네트워크 교과목이 실제로 어떻게 도움되는지 얘기하려면 '이거 읽는데 드는 시간이 몇배는 줄어든다' 정도로는 확 체감되게 설명은 가능할 것 같다

4
3

여태까지 내가 팔로한 사람이 내가 팔로하지 않은 사람에게 단 댓글까지 타임라인에 떠서 불편하셨죠? @xiniha 님의 패치를 통해 이제 내가 팔로한 사람의 글이라도 내가 팔로하지 않은 사람에게 단 댓글인 경우에는 타임라인에 표시되지 않도록 수정되었습니다. 아, 그래도 여전히 내가 팔로한 사람의 프로필에 들어가면 그 사람의 모든 댓글을 다 볼 수 있긴 합니다.

기여해 주신 @xiniha 님께 감사드립니다.

9

뒤늦은 서울숲하스켈 조교 후기: 왠지 모르겠는데, 다들 운동을 열심히 하시는지 몸이 굉장히 좋으셨다. 건강한 신체에 건강한 정신이 깃든다를 실천하고 계신 분들이었다.

...는 농담이고(근데 사실입니다), 커리큘럼이 내가 상상하던 방향이랑은 꽤 달라서 흥미로웠다.

마지막 회차에 하스켈로 웹서버를 띄우는 것을 목표로 진행중이었는데, 이를 위해 Monad Transformer(Monad는 진즉에 해치우고), Tagless Final, Lens를 모두 소개한 상태였다. 근데 저 개념들이 '왜 하스켈에선 이거 안 돼요? 왤케 불편해요?' 같은 질문을 회피하지 않으려면 꼭 가르쳐야 하는 부분들이긴 하다. 가령, 'Monad만 배우면 이제 하스켈에서 명령형 코딩 할수 있다'라는 이야기가 이론상은 맞는데, Monad Transformer나 Algebraic Effect 같은거 안쓰면 웹사이트등 실제로 쓸모있는 프로그램을 사실상 짤수가 없다. 그래서 가르치긴 해야한다.

문제는 저걸 다 가르치려면 상당히 빡셀테니, 나는 만약에 내가 하스켈 부트캠프를 한다면 일단은 저런걸 회피하고 하스켈의 멋진 부분에 집중하는 커리큘럼을 짜야겠다고 그동안 생각했었다. 근데 또 이건 어찌보면 기만이기도 하다. 그런데 서울숲하스켈에서는 어찌저찌 다들 따라오도록 구성을 잘하신것 같다 하스켈을 이질적인(긍정적으로든 부정적으로든) 프로그래밍 언어로 소개하는게 아니라, 언젠가 본인의 작업에 활용할 언어의 후보로 올리게끔 하려면 저런 내용들을 다 다뤘어야 할것이다.

암튼 그동안 수고많으셨습니다. @eunmin은민

7
1

https://moreal.dev/blog/review-learning-zig/

  • 요근래 안 하던 직접 메모리 할당 및 해제를 Zig하면서 해보니 Bun에서 왜 메모리 누수가 나는지 이해가 가는 부분이었다. (메모리 관리 잘 하기 어려운 것 같다)
  • 컴파일 타임 코드 작성하기 편한 것은 좋았다. (comptime)
  • 회고를 하며 내가 공부할 때 방황한 과정을 돌이켜 보는 것이 좋았다. "언어 철학 먼저보기"와 "표준 라이브러리에서 Write, Read 추상화하는 방식 참조하기" 정도를 먼저 하면 좋겠다는 교훈(?)을 얻었다.
  • 위 글에 애자일 이야기 글 링크할 때, 애자일 이야기 검색 가능하게 만든거 잘 써서 좋았다 :D
3
6

Hackers' Pub에 GraphQL API를 추가하고 있습니다. https://hackers.pub/graphql가 GraphQL 엔드포인트입니다. 아직 인증 기능도 없고 노출된 노드도 얼마 없습니다만, 차차 추가해 나갈 예정입니다. 다음은 예시 쿼리입니다:

{
  actorByHandle(handle: "@hongminhee@hackers.pub") {
    uuid
    iri
    type
    handle
    instance {
      host
      software
      softwareVersion
    }
    name
    bio
    avatarUrl
    url
  }
}

Mastodon 호환 API를 구현할 계획에 대해 문의 주시는 분들이 종종 계십니다만, 아마도 Hackers' Pub은 앞으로도 Mastodon 호환 API를 구현하지는 않을 것 같습니다. 개인적으로 Mastodon 호환 API가 사용성이 많이 떨어진다고도 생각하고, 이미 Hackers' Pub 고유의 기능들 가운데 Mastodon 호환 API로 표현 불가능한 것들이 좀 있기 때문입니다.

장기적으로는 GraphQL을 이용해 웹 프런트엔드도 크게 개선하고, 모바일 앱까지 만드는 걸 염두에 두고 있습니다.

7

Lee Dogeon shared the below article:

논리적이 되는 두 가지 방법 - 논리와 저수준(Low-level) 자료 표현(Data representation) (2 편 중 1 편)

Ailrun (UTC-5/-4) @ailrun@hackers.pub

이 글은 어떤 문장이 "논리적"이라고 할 수 있는지에 대한 심도 있는 탐구를 시작합니다. 일상적인 오용을 지적하며, 진정으로 논리적인 주장은 증명 가능성과 체계의 무모순성이라는 두 가지 핵심 조건을 충족해야 한다고 주장합니다. 특히, "좋은 가정 아래" 논리성을 증명하는 두 가지 방법, 즉 함수형 언어와 유사한 구조를 가진 자연 연역과, 약간의 "부정행위"를 통해 무모순성을 쉽게 보일 수 있는 논건 대수를 소개합니다. 글에서는 명제와 판단의 개념을 명확히 정의하고, 자연 연역을 통해 논리적 증명을 구축하는 방법을 상세히 설명합니다. 특히, 자연 연역과 함수형 언어 간의 놀라운 유사성, 즉 커리-하워드 대응을 통해 논리적 사고와 프로그래밍 언어 이해 사이의 연결고리를 제시합니다. 또한, 자연 연역의 한계를 극복하고 무모순성을 보다 쉽게 증명할 수 있는 논건 대수를 소개하며, 자연 연역과의 구조적 차이점을 강조합니다. 이 글은 논리적 사고의 깊이를 더하고, 프로그래밍 언어와 논리 간의 관계에 대한 흥미로운 통찰을 제공합니다. 특히, 커리-하워드 대응을 통해 논리와 프로그래밍이 어떻게 연결되는지 이해하고 싶은 독자에게 유익할 것입니다.

Read more →
12
1
1
3
2
0
0

Deno는 console.log()에서 %c 형식 지정자를 통해 간단한 CSS를 사용할 수 있다.

console.log("%cHello World", "color: red");
console.log("%cHello World", "background-color: blue");
console.log("%cHello World", "text-decoration: underline");
console.log("%cHello World", "text-decoration: line-through");
console.log("%cHello World", "font-weight: bold");
console.log("%cHello World", "color: red; font-weight: bold");
console.log("%cHello %cWorld", "color: red", "color: blue");
console.log("%cHello World", "color: #FFC0CB");
console.log("%cHello World", "color: rgb(255, 192, 203)");

위 코드는 아래처럼 출력된다:

Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
Hello World
4
5

어떻게 하면 React 기반 앱을 좀 더 안전하고 탄탄하게 만들 수 있을까? 우리는 그 답을 ‘리액트를 리액트답게’ 작성하는 것이라고 정의했고, react-simplikit으로 그 답을 구체화했어요. github.com/toss/react-simpliki

2

Lee Dogeon shared the below article:

Ditch the DIY Drama: Why Use Fedify Instead of Building ActivityPub from Scratch?

洪 民憙 (Hong Minhee) @hongminhee@hackers.pub

So, you're captivated by the fediverse—the decentralized social web powered by protocols like ActivityPub. Maybe you're dreaming of building the next great federated app, a unique space connected to Mastodon, Lemmy, Pixelfed, and more. The temptation to dive deep and implement ActivityPub yourself, from the ground up, is strong. Total control, right? Understanding every byte? Sounds cool!

But hold on a sec. Before you embark on that epic quest, let's talk reality. Implementing ActivityPub correctly isn't just one task; it's like juggling several complex standards while riding a unicycle… blindfolded. It’s hard.

That's where Fedify comes in. It's a TypeScript framework designed to handle the gnarliest parts of ActivityPub development, letting you focus on what makes your app special, not reinventing the federation wheel.

This post will break down the common headaches of DIY ActivityPub implementation and show how Fedify acts as the super-powered pain reliever, starting with the very foundation of how data is represented.

Challenge #1: Data Modeling—Speaking ActivityStreams & JSON-LD Fluently

At its core, ActivityPub relies on the ActivityStreams 2.0 vocabulary to describe actions and objects, and it uses JSON-LD as the syntax to encode this vocabulary. While powerful, this combination introduces significant complexity right from the start.

First, understanding and correctly using the vast ActivityStreams vocabulary itself is a hurdle. You need to model everything—posts (Note, Article), profiles (Person, Organization), actions (Create, Follow, Like, Announce)—using the precise terms and properties defined in the specification. Manual JSON construction is tedious and prone to errors.

Second, JSON-LD, the encoding layer, has specific rules that make direct JSON manipulation surprisingly tricky:

  • Missing vs. Empty Array: In JSON-LD, a property being absent is often semantically identical to it being present with an empty array. Your application logic needs to treat these cases equally when checking for values. For example, these two Note objects mean the same thing regarding the name property:
    // No name property
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Note",
      "content": ""
    }
    // Equivalent to:
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Note",
      "name": [],
      "content": ""
    }
  • Single Value vs. Array: Similarly, a property holding a single value directly is often equivalent to it holding a single-element array containing that value. Your code must anticipate both representations for the same meaning, like for the content property here:
    // Single value
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Note",
      "content": "Hello"
    }
    // Equivalent to:
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Note",
      "content": ["Hello"]
    }
  • Object Reference vs. Embedded Object: Properties can contain either the full JSON-LD object embedded directly or just a URI string referencing that object. Your application needs to be prepared to fetch the object's data if only a URI is given (a process called dereferencing). These two Announce activities are semantically equivalent (assuming the URIs resolve correctly):
    {
      "@context": "https://www.w3.org/ns/activitystreams",
      "type": "Announce",
      // Embedded objects:
      "actor": {
        "type": "Person",
        "id": "http://sally.example.org/",
        "name": "Sally"
      },
      "object": {
        "type": "Arrive",
        "id": "https://sally.example.com/arrive",
        /* ... */
      }
    }
    // Equivalent to:
    {
      "@context":
      "https://www.w3.org/ns/activitystreams",
      "type": "Announce",
      // URI references:
      "actor": "http://sally.example.org/",
      "object": "https://sally.example.com/arrive"
    }

Attempting to manually handle all these vocabulary rules and JSON-LD variations consistently across your application inevitably leads to verbose, complex, and fragile code, ripe for subtle bugs that break federation.

Fedify tackles this entire data modeling challenge with its comprehensive, type-safe Activity Vocabulary API. It provides TypeScript classes for ActivityStreams types and common extensions, giving you autocompletion and compile-time safety. Crucially, these classes internally manage all the tricky JSON-LD nuances. Fedify's property accessors present a consistent interface—non-functional properties (like tags) always return arrays, functional properties (like content) always return single values or null. It handles object references versus embedded objects seamlessly through dereferencing accessors (like activity.getActor()) which automatically fetch remote objects via URI when needed—a feature known as property hydration. With Fedify, you work with a clean, predictable TypeScript API, letting the framework handle the messy details of AS vocabulary and JSON-LD encoding.

Challenge #2: Discovery & Identity—Finding Your Actors

Once you can model data, you need to make your actors discoverable. This primarily involves the WebFinger protocol (RFC 7033). You'd need to build a server endpoint at /.well-known/webfinger capable of parsing resource queries (like acct: URIs), validating the requested domain against your server, and responding with a precisely formatted JSON Resource Descriptor (JRD). This JRD must include specific links, like a self link pointing to the actor's ActivityPub ID using the correct media type. Getting any part of this wrong can make your actors invisible.

Fedify simplifies this significantly. It automatically handles WebFinger requests based on the actor information you provide through its setActorDispatcher() method. Fedify generates the correct JRD response. If you need more advanced control, like mapping user-facing handles to internal identifiers, you can easily register mapHandle() or mapAlias() callbacks. You focus on defining your actors; Fedify handles making them discoverable.

// Example: Define how to find actors
federation.setActorDispatcher(
  "/users/{username}",
  async (ctx, username) => { /* ... */ }
);
// Now GET /.well-known/webfinger?resource=acct:username@your.domain just works!

Challenge #3: Core ActivityPub Mechanics—Handling Requests and Collections

Serving actor profiles requires careful content negotiation. A request for an actor's ID needs JSON-LD for machine clients (Accept: application/activity+json) but HTML for browsers (Accept: text/html). Handling incoming activities at the inbox endpoint involves validating POST requests, verifying cryptographic signatures, parsing the payload, preventing duplicates (idempotency), and routing based on activity type. Implementing collections (outbox, followers, etc.) with correct pagination adds another layer.

Fedify streamlines all of this. Its core request handler (via Federation.fetch() or framework adapters like @fedify/express) manages content negotiation. You define actors with setActorDispatcher() and web pages with your framework (Hono, Express, SvelteKit, etc.)—Fedify routes appropriately. For the inbox, setInboxListeners() lets you define handlers per activity type (e.g., .on(Follow, ...)), while Fedify automatically handles validation, signature verification, parsing, and idempotency checks using its KV Store. Collection implementation is simplified via dispatchers (e.g., setFollowersDispatcher()); you provide logic to fetch a page of data, and Fedify constructs the correct Collection or CollectionPage with pagination.

// Define inbox handlers
federation.setInboxListeners("/inbox", "/users/{handle}/inbox")
  .on(Follow, async (ctx, follow) => { /* Handle follow */ })
  .on(Undo, async (ctx, undo) => { /* Handle undo */ });

// Define followers collection logic
federation.setFollowersDispatcher(
  "/users/{handle}/followers",
  async (ctx, handle, cursor) => { /* ... */ }
);

Challenge #4: Reliable Delivery & Asynchronous Processing—Sending Activities Robustly

Sending an activity requires more than a simple POST. Networks fail, servers go down. You need robust failure handling and retry logic (ideally with backoff). Processing incoming activities synchronously can block your server. Efficiently broadcasting to many followers (fan-out) requires background processing and using shared inboxes where possible.

Fedify addresses reliability and scalability using its MessageQueue abstraction. When configured (highly recommended), Context.sendActivity() enqueues delivery tasks. Background workers handle sending with automatic retries based on configurable policies (like outboxRetryPolicy). Fedify supports various queue backends (Deno KV, Redis, PostgreSQL, AMQP). For high-traffic fan-out, Fedify uses an optimized two-stage mechanism to distribute the load efficiently.

// Configure Fedify with a persistent queue (e.g., Deno KV)
const federation = createFederation({
  queue: new DenoKvMessageQueue(/* ... */),
  // ...
});
// Sending is now reliable and non-blocking
await ctx.sendActivity({ handle: "myUser" }, recipient, someActivity);

Challenge #5: Security—Avoiding Common Pitfalls

Securing an ActivityPub server is critical. You need to implement HTTP Signatures (draft-cavage-http-signatures-12) for server-to-server authentication—a complex process. You might also need Linked Data Signatures (LDS) or Object Integrity Proofs (OIP) based on FEP-8b32 for data integrity and compatibility. Managing cryptographic keys securely is essential. Lastly, fetching remote resources risks Server-Side Request Forgery (SSRF) if not validated properly.

Fedify is designed with security in mind. It automatically handles the creation and verification of HTTP Signatures, LDS, and OIP, provided you supply keys via setKeyPairsDispatcher(). It includes key management utilities. Crucially, Fedify's default document loader includes built-in SSRF protection, blocking requests to private IPs unless explicitly allowed.

Challenge #6: Interoperability & Maintenance—Playing Nicely with Others

The fediverse is diverse. Different servers have quirks. Ensuring compatibility requires testing and adaptation. Standards evolve with new Federation Enhancement Proposals (FEPs). You also need protocols like NodeInfo to advertise server capabilities.

Fedify aims for broad interoperability and is actively maintained. It includes features like ActivityTransformers to smooth over implementation differences. NodeInfo support is built-in via setNodeInfoDispatcher().

Challenge #7: Developer Experience—Actually Building Your App

Beyond the protocol, building any server involves setup, testing, and debugging. With federation, debugging becomes harder—was the message malformed? Was the signature wrong? Is the remote server down? Is it a compatibility quirk? Good tooling is essential.

Fedify enhances the developer experience significantly. Being built with TypeScript, it offers excellent type safety and editor auto-completion. The fedify CLI is a powerful companion designed to streamline common development tasks.

You can quickly scaffold a new project tailored to your chosen runtime and web framework using fedify init.

For debugging interactions and verifying data, fedify lookup is invaluable. It lets you inspect how any remote actor or object appears from the outside by performing WebFinger discovery and fetching the object's data. Fedify then displays the parsed object structure and properties directly in your terminal. For example, running:

$ fedify lookup @fedify-example@fedify-blog.deno.dev

Will first show progress messages and then output the structured representation of the actor, similar to this:

// Output of fedify lookup command (shows parsed object structure)
Person {
  id: URL "https://fedify-blog.deno.dev/users/fedify-example",
  name: "Fedify Example Blog",
  published: 2024-03-03T13:18:11.857Z, // Simplified timestamp
  summary: "This blog is powered by Fedify, a fediverse server framework.",
  url: URL "https://fedify-blog.deno.dev/",
  preferredUsername: "fedify-example",
  publicKey: CryptographicKey {
    id: URL "https://fedify-blog.deno.dev/users/fedify-example#main-key",
    owner: URL "https://fedify-blog.deno.dev/users/fedify-example",
    publicKey: CryptoKey { /* ... CryptoKey details ... */ }
  },
  // ... other properties like inbox, outbox, followers, endpoints ...
}

This allows you to easily check how data is structured or troubleshoot why an interaction might be failing by seeing the actual properties Fedify parsed.

Testing outgoing activities from your application during development is made much easier with fedify inbox. Running the command starts a temporary local server that acts as a publicly accessible inbox, displaying key information about the temporary actor it creates for receiving messages:

$ fedify inbox
✔ The ephemeral ActivityPub server is up and running: https://<unique_id>.lhr.life/
✔ Sent follow request to @<some_test_account>@activitypub.academy.
╭───────────────┬─────────────────────────────────────────╮
│ Actor handle: │ i@<unique_id>.lhr.life                  │
├───────────────┼─────────────────────────────────────────┤
│   Actor URI:  │ https://<unique_id>.lhr.life/i          │
├───────────────┼─────────────────────────────────────────┤
│  Actor inbox: │ https://<unique_id>.lhr.life/i/inbox    │
├───────────────┼─────────────────────────────────────────┤
│ Shared inbox: │ https://<unique_id>.lhr.life/inbox      │
╰───────────────┴─────────────────────────────────────────╯

Web interface available at: http://localhost:8000/

You then configure your developing application to send an activity to the Actor inbox or Shared inbox URI provided. When an activity arrives, fedify inbox only prints a summary table to your console indicating that a request was received:

╭────────────────┬─────────────────────────────────────╮
│     Request #: │ 2                                   │
├────────────────┼─────────────────────────────────────┤
│ Activity type: │ Follow                              │
├────────────────┼─────────────────────────────────────┤
│  HTTP request: │ POST /i/inbox                       │
├────────────────┼─────────────────────────────────────┤
│ HTTP response: │ 202                                 │
├────────────────┼─────────────────────────────────────┤
│       Details  │ https://<unique_id>.lhr.life/r/2    │
╰────────────────┴─────────────────────────────────────╯

Crucially, the detailed information about the received request—including the full headers (like Signature), the request body (the Activity JSON), and the signature verification status—is only available in the web interface provided by fedify inbox. This web UI allows you to thoroughly inspect incoming activities during development.

Screenshot of the Fedify Inbox web interface showing received activities and their details.
The Fedify Inbox web UI is where you view detailed activity information.

When you need to test interactions with the live fediverse from your local machine beyond just sending, fedify tunnel can securely expose your entire local development server temporarily. This suite of tools significantly eases the process of building and debugging federated applications.

Conclusion: Build Features, Not Plumbing

Implementing the ActivityPub suite of protocols from scratch is an incredibly complex and time-consuming undertaking. It involves deep dives into multiple technical specifications, cryptographic signing, security hardening, and navigating the nuances of a diverse ecosystem. While educational, it dramatically slows down the process of building the actual, unique features of your federated application.

Fedify offers a well-architected, secure, and type-safe foundation, handling the intricacies of federation for you—data modeling, discovery, core mechanics, delivery, security, and interoperability. It lets you focus on your application's unique value and user experience. Stop wrestling with low-level protocol details and start building your vision for the fediverse faster and more reliably. Give Fedify a try!

Getting started is straightforward. First, install the Fedify CLI using your preferred method. Once installed, create a new project template by running fedify init your-project-name.

Check out the Fedify tutorials and Fedify manual to learn more. Happy federating!

Read more →
13
0
3
1

Hackers' Pub은 검색에서 몇 가지 기본적인 문법들을 지원하고 있습니다. 문서화되지 않았기 때문에 한 번 정리해 봅니다.

문법 설명 예시
" 키워드 " 따옴표 안에 들어간 문자열을 띄어쓰기를 포함하여 찾습니다.
대소문자는 구분하지 않습니다.
(따옴표 안에 따옴표를 넣으려면 \"와 같이 이스케이프.)
"Hackers' Pub"
from: 핸들 해당 사용자가 쓴 콘텐츠만 추립니다. from:hongminhee
from:hongminhee@hollo.social
lang: ISO 639-1 해당 언어로 쓰여진 콘텐츠만 추립니다. lang:ko
# 태그 해당 태그가 달린 콘텐츠만 추립니다.
대소문자는 구분하지 않습니다.
#Fedify
조건 조건 띄어쓰기 양 옆의 조건을 모두 만족하는 콘텐츠만 추립니다(논리곱). "Hackers' Pub" lang:ko
조건 OR 조건 OR 연산자 양 옆의 조건 중 하나라도 만족하는 콘텐츠를 추립니다(논리합). 해커즈퍼브 OR "Hackers' Pub" lang:ko
( 조건 ) 괄호 안의 연산자들을 먼저 결합합니다. (해커즈퍼브 OR 해커즈펍 OR 해커스펍) lang:ko

구체적인 동작 방식은 Hackers' Pub 소스 코드를 보시면 더 자세히 알 수 있습니다.

4

개인적으로는 k8s쓰는 가장 큰 이유는 개발자 복지라고 생각한다. 적정기술만 쓰면 대부분의 사람들은 뭔가를 실 서비스에서 경험할 기회를 잃어버린다. 아니 이건 됐고…

온프레미스 클러스터 오퍼레이션 부담이나 EKS같은 서비스의 사용료 걱정만 없다면 쓰는게 무조건 낫다고 생각한다.

일단 k8s뿐만 아니라 컨테이너/머신 오케스트레이션의 세계에서 앱과 머신은 좀 더 잘 죽어도되는 존재가 된다. (물론 stateful한 호스트와 앱을 최대한 stateless하게 하거나, 상태를 분리하여 격리시켜야 하긴 한다)

그러면 docker-compose로 충분하지 않느냐 말할 사람도 있겠지만 처음에야 docker-compose 쓰는거나 k8s 쓰는거나 그게 그거지만(오히려 k8s가 성가실것이다) 마이그레이션의 때가 오면 난 그걸 감당할 자신이 없다.

물론 자신만의 가볍고 쏙 맘에드는 솔루션을 고집할 사람도 있을텐데… 난 남들이 다 쓰는거 쓰는게 편하다.

4

HTML 되는데 <b>를 모바일에서 입력하려니 번거로워서 뭔가 에디터(?)스러운게 있으면 좋겠다고 생각했는데 그냥 마크다운 문법을 쓰면 되는 것이었다 😅

3

https://agilestory.blog/5095853

사람들은 (상대적인) 비용 중심으로 생각하는 데에 매우 익숙해져 있습니다. 우리를 종종 실패하게 만드는 휴리스틱스(heuristics)입니다.

그런데 그런 것을 따지지 않더라도, 이상하게 이제까지 제가 살면서 나의 배움에 대해 "질러서" 후회를 한 적은 단 한 번도 없었던 것 같습니다.

1

2025() 오픈소스 컨트리뷰션 아카데미 參與型(참여형) 멘토() 募集(모집) 公告(공고)가 떴다. Fedify 프로젝트의 메인테이너로서 멘토()志願(지원)하고자 한다. 志願書(지원서).hwp 파일이기에 큰 맘 먹고 한컴오피스 한글 for Mac도 購入(구입)했다. (아무래도 앞으로 .hwp 파일 다룰 일이 많을 것 같다는 豫感(예감)이 들어서…)

6
3