XiNiHa

@xiniha@hackers.pub · 37 following · 24 followers

GitHub
@XiNiHa
Bluesky
@xiniha.dev
Twitter
@xiniha_1e88df

We're excited to announce the release of Fedify 1.5.0! This version brings several significant improvements to performance, configurability, and developer experience. Let's dive into what's new:

Two-Stage Fan-out Architecture for Efficient Activity Delivery

now implements a smart fan-out mechanism for delivering activities to large audiences. This change is particularly valuable for accounts with many followers. When sending activities to many recipients, Fedify now creates a single consolidated message containing the activity payload and recipient list, which a background worker then processes to re-enqueue individual delivery tasks.

This architectural improvement delivers several benefits: Context.sendActivity() returns almost instantly even with thousands of recipients, memory consumption is dramatically reduced by avoiding payload duplication, UI responsiveness improves since web requests complete quickly, and the system maintains reliability with independent retry logic for each delivery.

For specific requirements, we've added a new fanout option with three settings:

// Configuring fan-out behavior
await ctx.sendActivity(
  { identifier: "alice" },
  recipients,
  activity,
  { fanout: "auto" }  // Default: automatic based on recipient count
  // Other options: "skip" (never use fan-out) or "force" (always use fan-out)
);

Canonical Origin Support for Multi-Domain Setups

You can now explicitly configure a canonical origin for your server, which is especially useful for multi-domain setups. This feature allows you to set different domains for WebFinger handles and URIs, configured through the new origin option in createFederation(). This enhancement prevents unexpected URL construction when requests bypass proxies and improves security by ensuring consistent domain usage.

const federation = createFederation({
  // Use example.com for handles but ap.example.com for ActivityPub URIs
  origin: {
    handleHost: "example.com",
    webOrigin: "https://ap.example.com",
  },
  // Other options...
});

Optional Followers Collection Synchronization

Followers collection synchronization (FEP-8fcf) is now opt-in rather than automatic. This feature must now be explicitly enabled through the syncCollection option, giving developers more control over when to include followers collection digests. This change improves network efficiency by reducing unnecessary synchronization traffic.

await ctx.sendActivity(
  { identifier: sender },
  "followers",
  activity,
  { 
    preferSharedInbox: true,
    syncCollection: true,  // Explicitly enable collection synchronization
  }
);

Enhanced Key Format Compatibility

Key format support has been expanded for better interoperability. Fedify now accepts PEM-PKCS format in addition to PEM-SPKI for RSA public keys. We've added importPkcs1() and importPem() functions for additional flexibility, which improves compatibility with a wider range of ActivityPub implementations.

Improved Key Selection Logic

The key selection process is now more intelligent. The fetchKey() function can now select the public key of an actor if keyId has no fragment and the actor has only one public key. This enhancement simplifies key handling in common scenarios and provides better compatibility with implementations that don't specify fragment identifiers.

New Authorization Options

Authorization handling has been enhanced with new options for the RequestContext.getSignedKey() and getSignedKeyOwner() methods. This provides more flexible control over authentication and authorization flows. We've deprecated older parameter-based approaches in favor of the more flexible method-based approach.

Efficient Bulk Message Queueing

Message queue performance is improved with bulk operations. We've added an optional enqueueMany() method to the MessageQueue interface, enabling efficient queueing of multiple messages in a single operation. This reduces overhead when processing batches of activities. All our message queue implementations have been updated to support this new operation:

If you're using any of these packages, make sure to update them alongside Fedify to take advantage of the more efficient bulk message queueing.

CLI Improvements

The Fedify command-line tools have been enhanced with an improved web interface for the fedify inbox command. We've added the Fedify logo with the cute dinosaur at the top of the page and made it easier to copy the fediverse handle of the ephemeral actor. We've also fixed issues with the web interface when installed via deno install from JSR.

Additional Improvements and Bug Fixes

  • Updated dependencies, including @js-temporal/polyfill to 0.5.0 for Node.js and Bun
  • Fixed bundler errors with uri-template-router on Rollup
  • Improved error handling and logging for document loader when KV store operations fail
  • Added more log messages using the LogTape library
  • Internalized the multibase package for better maintenance and compatibility

For the complete list of changes, please refer to the changelog.

To update to Fedify 1.5.0, run:

# For Deno
deno add jsr:@fedify/fedify@1.5.0

# For npm
npm  add     @fedify/fedify@1.5.0

# For Bun
bun  add     @fedify/fedify@1.5.0

Thank you to all contributors who helped make this release possible!

0

소프트웨어 개발자들이 자주 틀리는 외래어 표기법.

영어 틀린 표기 올바른 표기
app 어플
application 플리케이션 플리케이션
directory 디렉 디렉
front-end 트엔드 트엔드
message
method
release 릴리 릴리
repository 포지 포지

또 있을까요?

0
0
3

개인적으로 Hackers' Pub 행동 강령에서 내세우고 싶은 곳이 있다면 이 부분이예요:

구조적 차별과 불평등에 대한 우리의 입장

우리는 현실 세계의 구조적 불평등이 온라인 공간에도 그대로 반영되고 있다는 현실을 직시합니다. Hackers' Pub은:

  • 성차별, 인종차별, 장애인 차별 등 우리 사회에 만연한 구조적 차별이 존재한다는 현실을 인식하고, 이러한 차별에 반대합니다.
  • “모든 사람을 동등하게 대우한다”는 명목 하에 이러한 구조적 불평등을 무시하거나 부정하지 않습니다.
  • 사회적 약자와 소수자에 대한 적극적인 포용 정책이 진정한 평등을 향한 필수적인 과정임을 확신합니다.
  • 차별과 혐오에 대항하는 발언과, 차별과 혐오 자체를 동일선 상에 두지 않습니다.
  • 우리는 이러한 구조적 차별이 결코 정당화될 수 없으며 반드시 극복되어야 할 과제임을 분명히 합니다.
0

한국 연합우주 개발자 모임(FediDev KR)은 이름 그대로 한국에서 연합우주(fediverse)와 관계된 개발(프로그래밍 뿐만 아니라 문서화, 번역 등을 포함)을 하는 사람들의 모임입니다. 실제로 Hackers' Pub의 개발 논의도 이 모임에서 처음 나왔었고요. Hackers' Pub을 통해서 ActivityPub이나 연합우주에 관심이 생기셨다면 한 번 참여해 보셔도 좋을 것 같습니다.

참고로 올해에는 아직 개최한 적 없지만 비정기적으로 스프린트 모임도 하고 있습니다. @sprints.fedidev.kr한국 페디버스 개발자 모임 계정을 팔로하시면 스프린트 모임이 열리기 전에 미리 공지를 올릴테니 미리 확인하실 수 있을 거예요.

0

XiNiHa shared the below article:

복잡한 코드를 단순하게 줄여나갈 수록 발생하는 버그의 빈도나 심각도가 점진적으로 올라가는 경향이 있다고 느낀다

@disjukr@hackers.pub

<metadata></metadata><style class="style-fonts"> @font-face { font-family: Xiaolai; src: url(data:font/woff2;base64,d09GMgABAAAAAAQsABEAAAAADHQAAAPRAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbHhweBmAANAgKCY4uERAKggCBcAsGAAE2AiQDCAQgBYJeByAMFBsxC1GU7kmc7IuDeIxnknghkogrbGm/yUrQr2+4AWU2eJ7yqver0gMwA3Apn7YXEm9wGAyuCeZhwkEGwsU2dSmhL3X5FByVFyffuXywYwJeQMyQoTnpoLuJgfyOKhWW//9e/Zj7X6bY2GJ5Of9miUZkbxpzAa2CtITVcoaFqhurXC9g/6hepcHKv18JArgBAAIoBEEAF8KEkYqVazbECQPAsgAGzmt9XaZjA4UCpG8i309uOM0ocUCLC1gJ31RzqzSh7De81lD3tfpC92V6W0OWPbuHsqn/gMkGKEADfjQCDQ1MYAikca2hZbFc5pbVCzb8vB95kDr7cavbdIfI1Gb7xRoTVMwDuKHbtE7bj6SaZqVuFbdL27T9qNT9yBLkAJfWdVy700Qx06rdzWzlfbm7EZe4Wu96WLOhOJ9+xPkVXpYdZ+dBjnJAtiBG0prBnHwXk6+L3PLPAT0X1/Eb3arbSwffWdgBpPrxjfrx0gGZA3ZJV1L8fZrrAk8xJCo2CO2ZixRoEyvJBLhi1rEYWk+GQEVlM8HseR1XQ+yb9po9oYNuSUhgByw5kOd7rUXIwJD6XPsIkaA0GKItbxm0g3c5eOCzniQQlKUwZf9fvDcdIiIqhoTElk8ZdIbPORRQsgVfK0DXiIZqoSOt5XsGPeBHDhPM7MTXBdArYqN2OMhu+ZNBX/ibwwMv+/DV0UxgnPUqQOOEIohYFhoABYCRrTcD3dAEblOZSX0myohFy5YFhja87TLxrIwInBvMJuxrci9wXhMDa2+Zt6/KPddJcXOwh93e3mhi+7suFbsqAXNlHk8GrpuUlKXDyXnzxMSG4KglwcHJX1NpDXY/++npl6OSbbpdx9Twl0YtlVflZ2Iof8P3TeMj8t4YVVHP+GzM1oEJF71dano2q9fJYenSwaMG9e/tNXNe+uZqplNblXM9YldoZWfH5iVXzWsss3Cq2VqPvWtz3W6O96scvXR4x/tBTsX+b35Db6vDktwqrvBu41nym3DV7wH3V32NBXiVEjfTem3txl/dD3BCLfD+XpLGtwHp/8+PEVwP6qMX0JL1lfpEYSKxbOl1zznSnEYMZyDZw0Ij+DmrUXiwX6NJN3HfGNbYGMon5GC+D5rqpp0+emqnmwYqaKyT/gbopo/eTDnSZclSQkWddNBJL+1h15QrleWzZQZOGKk8vTtQBf1hqF0GffKmrA1imUE+W34jZCbPy5WmvW4GXiJi8ocV81B9mO9i0MST/Ze5uKKPvoYZdtNFVwP3N1z0RVNNTVXRhMZ1ZPWVqbKfxA4gAvzAJU0KAAAA); } @font-face { font-family: Xiaolai; src: url(data:font/woff2;base64,d09GMgABAAAAAARQABEAAAAADJAAAAP2AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbHhweBmAANAgKCY4uERAKghyCDAsGAAE2AiQDCAQgBYJeByAMFBtNC8iO0UvPCUWZcpJwSsfD136v5+7uD5ACBB8XFy6xkIDjW1thm7rqql8boQqoCHVt3s/nZS8DKMXPFFRmSv5168xa0+P+hZV1VjycCp72Cp8q//8vbTB3fppg6WL5OfPORDxCvVJpVjtQBUkIV8NbOmDFEC/6jvqO6lUarPy7KNAGFOUkjJEWg78JtemtagKFAlUFA7fn/CIOkJIkxPXEUJcaiokUDXK44BFWts+VtkLh0Tc8Il41dx1EPwkHrkmox5+GQaqlY4ySIiGjk1igk8QoUBTB0be5CihtdK1eqelEV1kHWNwSQLPf9oCiHp0XqLmHArBDz0EjX10UJwC1iJCHY3lqSZwAokUBqHkmTgDxIvLy4jBvQ1sIRgSR8i6CvKivvYuXhCW+1nd1E5yAeGg8xyEems4xfCGmnY1ujLPjBJBswSRZgmEJ+RYzn4985EycANLFSsSLw+va6tt4P0vzhSymGQZ5+Hd1bfw7lmYwjhNAdklfUt3OOa4LeU42T5KKDWKNzEcqmJBKNoSBC4TQJARbZwUKqKawIMDsaXGYR6D8++0t/wRDo2wIIx6DaQazcQIoFysb2zwszTAYqFTnBVB8rkbBqaFhavm1AllH10swwCgadCZVIQfu/H8L3ps3EyycFTZmld8ukB10pwQXaNGl1ADABxKCuGAgFiw/I5Dd9BAJoQgTQ5UaDvgIQiQXhWgWJX+MQI6lx0mIR4IYr9RE4JrUBJDt1yW9qKqSQQIlwFXcAbTBFZceY7ixnRxzD9kuUf09p+3X1XtPPzkL/sMjlo7W4WH7caplSEU6j4C/72wraRkKPzIQy6tI9J/9OBxy+rT9+HX17q6j4B89ASdPPx+0HrnRTLLHh+3H2/59eAOHT3ct8o2M6KsWD6TTOav/HfJCa+uxj7i3+XXL1qKz4hLxQlFS8xGP+G5rRWK6UaY82PH1m9uTu91nOVhQf/yXKQglF5T/j6g9nvjK/togMmxNN8ZPZSoO3Kmb9BQGOo6/dCks8rXS+gMvNZrnz9prpE/mMpqgEHCreiI2imYF8mDUi4HY+sX63P8Kdf4NHy7/CAXgW0zYobnv1VyZzqsBNEkg+Nlky/8B/fG3U8ENoK78wFrWTemfGcwitS6D7m2NLhCL0ox5Y4U1dPpiTTrUrdkysl2GrTX3mWIN5ufAOZx2Ky3TjtOoRItua6zFWWkFJEW8JElysLp16rZcB2wjqWJZOVliUM7hnd5ep8QaFGvPsLKMJG2QSgzKydIbMZTcLFWcDpx1l0ggflw2jzXAcq/1Ew+uWebjxpVW2ezM6dVn3fbNi15EqrQp1UrM9VZdl6hRF7kQIAgWqPKwAAAA); } @font-face { font-family: Xiaolai; src: url(data:font/woff2;base64,d09GMgABAAAAAARsABEAAAAADIwAAAQTAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbHhweBmAANAgKCY4uERAKghiCBQsGAAE2AiQDCAQgBYJeByAMFBtGC8iuA2xwfQMNpVXW2LKcYgxDZLO05aK8Fyc+hxsPfGusPzuYWqWJaSNCpZHJRCohWiZ0a3KX7nVN6/9b93VTqdaYr85fDVpCBv6oBrDsjLWDM86IS2qvtTBxzf9eZnKf73zDDXBtlaIDjhJNng6vUVCiSzjCCEuf2ZUP/iRgK+9/OP/CFQeacLHk/n/35se9f7YJlm4LuLPnv22iKzKzvVBNYrUDVZCEMNo7WMUoRzxfPYPuJgXr71cigAIAASQYhoE8s8zCpmbyyuDLBFIABq6P8XEYQyFhgHCZ2J55AmFBErzBgwtEBWuW9jIrkfQioXNiMwXRA2w6M8GDn++AJNP4h5YtU2RjBBU4lgQyEciwlMGLJIU7k6/eyzTJ+5HPAAYWNShre2cYNuxr9vcMZBovg4KfnvhrgANE6XaaYjWoAQ1ogH2WvwY8QBmKM6OkyiFIksxeIymDPCvXKJNYtr7KKUUgKZT2bFIo71kqScx0NjiFiPHXQLZgZl0K2KHqxaxakvf8a30GOpBLikdRVXGVGszMVElpYqZlUbq6tVZXt6bElhD+GtglZdLvs6ddF3gD6yNlwgaFUiZJT0hZqTssuOKmK5YOYe2rmVBTtjRmL+ZM16zX2se63ZqJRCz4WWKmJdL8NYgDuaVV6WkVQSUskD43PggmAkcGhZEjT1XINDpdAwNMlWFhGZI4hBr8W3hvnk3gcFzwGDfyfIUsoAs1iCBWRXqVAF5KkHFyKJg88kqFrKKrNWigVTV61QFeTzBwRpiYMfJmhWyhWzXYYFdtenWAYOe9AJEvwzQsBQbQYiADuP0UXAAK4C2xZ/aMFplwrGLHy76t50fSqTrEsLM68dW7PbrXzpF7LzxZfnTedf/tmN425CkQgyM72FTTsw+P3lfXFbvu5ZEG7zjbytd1NWa85HJGQOXu8hfk5wNXDP3Xg/d+LThFv9Ne7SxprqtLTv9BIzVNTPGWfyg9rw9MOtgftnfaKPHWjuGGLTsn3sWjOJ2zA8Z1Jqc8rLavjrHu3XM/lz6S6e0Pjo7YNLT+GV3rxJTO8iluL3j231n7+ry6++y0CN73jiNsb8GVAK7SonITDMSGeaU724aXj418JZD5R/g4vDqAn+bOLf9n9I8tkMMABPjT1DN+G+vfXoGbQpPitTabTJIvujELlTGZdq/LtgjHMvwAfd4WbmaSB27DGNrNBZCbzWG3l04TCt7m+2CPbaUGtVaylUpVwalZC1uDeiRUgGDBYqVxWs2pzio4TsIsZN0QQYGApYzp461SNcPCygwNXRK8QSUo6IaIaBQoOVgYf6vYWi9RIr67GF4ogV23tok7Ni+T3N6gUZe/zc2jdfPRix5K8lTJVIlzE6UJMvLM0C8hAwekJtAPAA==); } @font-face { font-family: Xiaolai; src: url(data:font/woff2;base64,d09GMgABAAAAAAR4ABEAAAAADLwAAAQfAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbHhweBmAANAgKCY4uERAKgkiCMQsGAAE2AiQDCAQgBYJeByAMFBtyC8iOwradM1YI0gkehDEKJx6exv137swH0aT6E7SKUIhwKHCoW+rb+ENbqMMVnp/y8t802d3Sq0llTz9bp2Z6nU+fxHxP69MHfDjR/v9f5uDe+dsESxfbv2feNtEVKb2md5AKkhBGw61oQhWjHPEMewbdTQrW369EoAkQSEIINBiigdY388rAUBWUAgNX5/w4NoKCJCHOJboKhEXduhQ1crjgEXYdOJpZCUn7wXQVd650gegeNq7m0cM3TwBSdfqPsS3D5GBGF04k9rwxoEoRk/3g/wVX1+6u3qnW5f3ICsDMHgPTI0fPIPSAMPT/OoO04Wkwlc+c9meAZjBOF9NktHqWgcEMAzQl+DPgZnCGzLlnlFS5Ekwxza6nOAM319TLvHsSbH1RAyWBWIbSoyKWoeyoICcRtbOvgZAYfwb8FowvUAKruGUxawmS9/zrz0Axk4tlzqOoqrhKltLUclIaUQsCTpeXiqrkpTS1QIg/A+Ulg0l3XHS8LlRF5RTJGG6QLGVB0iGUVgquAtxGqZq6EutchoCqksoMZm/k3NMZkoraR5KroEa3cRVcBaIWSJo/A6OZ3NKq9DQRoEGYNP5c32CoMuRMYMpM3G8mkM3pFhIsYSVaaqwVSRw4k/4WvDdvQ7Dl7GDP7NzvIJAd6U4SnKEWneWqAbyWoOP0wEzvfkEgu9BdJbjBXXSTqwfgPQlenDd8mLf7fQWyH91fQgACxQC5BgHH4CGAbD8t6YNKkUECVQEu4TqgCV6zasDHbLKdVlz3WUBTicdnI6+YrAm/8Ei7D6S4I27nG6n9djRVufjE9cnx1jcEqWwrSPPlj7y3gcSMqcf2hZJDx4y33n3odaTmyiyaMqZhV0FyulfHd+1GKJ430GN2Q73jybLCHpQWHHaQ/4jdtkcg2e48kB/aJno8ufglzntbaW9FZVVSxLPMsA9PAgOLtr+I/Zip7P1jF1C+ap93wW6L1AuGHl56h1URqna74tBMn5llJlZW5l1jOzZ+j+vlu0pgyozV/PfuYb/amQJy1sf28/NVP83VW+K1AXU1vK3XWUvjc0dLLXLqKlddAp/OWZs/KuIqbzyeDAqGJMDZcjM7G/wUyEPebwcLTVa1xn01NMkfAa/3v3MD4I2v++b/b4s3/zd3A3USCP4/hapvA9r/X88KbhgtyLehky2k9IUnZpFdkhH3qlrHcaKqHlOeF+bQ5bI5aVExZ3Po4aphc7VrrJM15vvAUaIandrUEJVKVaFBj16iTh2wUAGCBYuVpkGdBu1q4RwWxo+1QwQF7VgZ0+f6pOpByZoMnW0seINsUNAOEdFI4uRiYfzVEvVdIo356WJ4sgS2m/RPPLFnWZDrO3UZthc1adZ3+OhFL8XyVMlUieP6K+cEGVTAazqADrYwLsE0AA==); } @font-face { font-family: Xiaolai; src: url(data:font/woff2;base64,d09GMgABAAAAAAQQABEAAAAADFgAAAO4AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbHhweBmAANAgKCY4uERAKgWSBWgsGAAE2AiQDCAQgBYJeByAMFBsbC1GUDUqH7Odh7IxoGdAyZ+VKI31IG+puNXjodbX387MnouFEl1MRRHRhqES1VKlMtIKOISiD241DCQiDhm2jwug4h/F0czljYSdX3cOj22euhLmER84fKB1c8IKrNX2Z/QvKuEhg43KZ35CxMYpRITpAoVCYRMnYAuuqKkBZq8nI/qhehcHM328JArABAASgIBAIwAofPkRBUUUDFqgAug4wcreUd1uJARQUQExIladIyGJIEWZI4QF2grmKLW4m5xfRR5UT9AkgJ7CyRxU73jwBxaD8BxoDoAAScCERqVxFA1UgmucW6TrL58/dvGjABX/Jg2XGhmLV0FeID6vQ7wpM/oeF7F5aBRoB/MWJ6LEKMiowAq2CjRCIDA76ZBhCLSzdh1oA581e1ODU3XmvQ9haAuGjCwg/oeRw1l06MJetQvYgaipshXm4riZXN3nNv1WosQEiQ7rUIzGZrOQ4ziKCl0tdkksui8xWYbd0p6XbJXVf4GkWQWJih/CAuUmRw8W1lEE4FYZlGMj2QRVQUU5WWTwqg70KU7e7bjKYRSmD4jLOIjurSGNjQN4BGXJY/a5zhEhQGiyibW8zaAfvcvDAZz1JIMihsGT6X3w3HSIiKoaExLZPGXSGzzkUULIFXytA14iGaqEjre17Bj3gRw4TzOzE1wXQK2KjdjjIbvuTQV/4m8MDL/vw1cQYYB77IEBigYIHoetIABQA1Hy2EnSjDTyJkuE484Tf5ramh5g81rocs1ocst7tcNtjscB9g/USz00m68ZGW+G6znOddMGOy9yXBAcrCxu3Ydo4LnlJszd/wmpx0Fo28Wj8j4sxpU9+M9e+5xrm3SWp97uPpC0etX5yQr3BR/ZEJE6cYhE2rW683VRG197ZcNbO7vTaWA/DvhvyT/f61zFWXh4pqealzR72892/TojKtRZ5lvM3pLQ8emCdXG6oaZ1i9rys9AkfYnH0Ubd9xl8La/kV8HTP7yCAD5HBq/WP+q7+kxMBC5QVPjMlpf4dkf7/uU94XjRALmUUGyjKD6GYiHic9Hl3mdNGIlRLIMLzUidwcVmnYMcpnSTK4lV1XGdgBqewmfV50GKkXuON0WukevmaDJpkspHGG0cTL0qsWOkKDOo3aKw+2NMkMLJCnJiwkK5ofm+KfJNQuDeH8QVN7A7xmLAQJ6kV1tLjEgzVZ6Qpt4hqfLc0Hq6DheGmztw6aZ2bq8abYKbpSMONMGX10E1P1FRoUayZxHOlTyBG9RRhI8EPXMAqQyQ=); } @font-face { font-family: Xiaolai; src: url(data:font/woff2;base64,d09GMgABAAAAAAP8ABEAAAAADCgAAAOhAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbHhweBmAANAgKCY4uERAKgTSBNQsGAAE2AiQDCAQgBYJeByAMFBv2ChEVm7/JfibwZHiNAF8Ur3BUq+BQX17m/XLwUO9qvU5ndgfx9SxVHURncF2XBFMJ4ha8Mf2GE5e/OzJuDriphvh1jYEGWBllHwBXlx5m/9IESxfLZX7nIx6hXqk0qx2oguQJo+FmoXDFKEc8zxIduA2s6t+vBAFUBwAEYCAQCKAqqaQi0jLyyghjAgQBwMBxKV+JrVBgYABiWqLRnCI8YIgYpHABB8FMnpNZSZ8bn0g9clowDaJ3snWYKY58/j7ACBlvAEUIMAAJxCERQBwmCjAxEXDjkyAAudzGqxdCxPFHakAkVyMWOVeF2OxqEazWXPXvICKHDolqYESp9PFpvhgW1aCIBrawoxoiojJ82TijxGnoKk952aM8laHGDR/lm40TV+tVoz23tfL5nPHK53dsv49rOQujXbdbVENuQc2k51JOTVhMJ7jEe/4f1VCRXOXLJkVOseMvS7P8PmmuZdsq3X9c5PiP0yzbdaMa+pKupLh4fNJ1gUnRLcQou0GoVHaJBdfzKsmGNk14nuU1dC1RLZAUfTKa2cOycboWy4raa8sa2hYnG9rqJa5lu2lRDRfJLXXS0yzbdqGGF9ewn6tgGQFEKggvEbSMV3iooHGqSpf1YbC37Ld4b9ZAmIwFNrWEdwjaxXs8fAg4X6oh2AgRMwmkNBE+I+gcX/AooeJKqdZgG0TLdNDTTviBoEf8xGOGhZulusJ2uwmQhDFIQgQBEgADADOb7g7kQRP4+0ajxJkbK24lXH5PLLf2ZL5fbcO7xMUve6es7N3y8g0dT1XbtQ+8c6frVbys6iXsF8svVDs1/Mt3rV3guJ3Pb9k5E+UXifPjfm7TZWfDdn7sgazCmMRQKDGv+iXxsVH9pdX/rDi2c03tHV44PGFdZkqvfaF2rx8s/EOrllLD7mYaYkVst7t1qzo1rwIABCBTmwcfMHdorR7/hqvJHwA+2G+NAL6u8fbgm6C5+Z+cDoQxFnj/IEnz3wH1m+8icD3cRJ7glpuG8TNNBSpi7TSveyyWAUiEWQVowUeFURDHS6NBTbRR0gpv2Rw2hphH3VCM+T4ZnPHDp04aPr60f8XoGTPHT52itNdKW211lzZ65OjJI6iudBCR8+20CfLdZUyvz+o/g0PDM5iaV9puEGsT5Nvp1AipZL8OokaMn3WJiFK266aESig/dvbEjTOWuRTt1GnzrcePHTdr+dxFDyp5TmaldJ0F09qo6DYNYjCBT4OWAAAA); } @font-face { font-family: Xiaolai; src: url(data:font/woff2;base64,d09GMgABAAAAAAQAABEAAAAADDQAAAOoAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbHhweBmAANAgKCY4uERAKgUCBRQsGAAE2AiQDCAQgBYJeByAMFBsGC8gepyt6cCTJgI18vrb0D0NcN+91iKiSjdoS803FnNTsZHajTm+qwuDNvd49gIEthEji4fP2ViVWIOuLlVV34SzQIQaejCXOvl/Tk7cJywgHbFx+5l7I2BhDRC4F9ihUlawiXd16QFmrycjeML2Ewfz7loAArAEAQAAcgiAAS168kE1fXA1zPACaBsDI3bS/SJaAAQ4HIDXVNtzJfI1LpqDJA9yIXcUlrw6Z71H6qHSYNgygFUtaebLuwQ2AG7n/AMYEwAEowBlFUg2PgefJePdRTWOVI23ztgln/CUbQOiuQr4MOwlZJKlEm63imQ9ARu/mMBUwGMs1igppDVOBQgXMQpgKHEyvUD99pcFHYjKTCzplpme9bZ0K74cDnffqkqUIpoAWI1PwFkHJlHTuvi5JSg5TQfaQ8nlZghXWt1raFyCv+S9MBRNFTKH+5YYKgzJN1CmZoqQTBJarHCw3KAdFnSBJYSrYWwbSzmSj+33BFdhM0vwOiSoWIG1Jlmt5HwFuk2Wd7CPZF6oEVJ5ZUrF4I/XLVcm08u7hNB9Bh/I+grhK0gmSGKZCiKIZ+VwRqgUpIf6ud/BynpEgSyXXKxxZpWsCdBgSXWnymQwcTftf+G7WItiMA1fquN7jyD49EBAikoRijQGbEFImQy7NXF9w5JJeCajRSGqxtoDtCD0zYJQOrp848kxfBKzYJKtYd2B/vCIAyhzHHfk1FABwAIAHEEAOoAbawIPIOvGxpXzddRVZ7bb6hk3tdNfp+yzlfZ5rr5Ppom/l0r/arvffsMqv7Me33Xdz9VXnadc8gmtqrh3DtD2W1XiU/k1rYoZZJ1remZHDu0+wt+33z6Y8qW0MNBPLcl5Xr7b8FO6t6+KGEyYOpjuGUD7br9ff1LL60Bn+Ei8EWOxIt8g0WYaf/HYY8OAUyUl3i3+OViNMnM+hU5DTmNI7LXap38yt6EsAuLnpvS8APAnxW6Y91YL4v3Q4AHMcACBgktTBfyOq/29De8S8CF6BPqBMx3VvEJDBHD5axgFaM/XokcECQLDbpYlwdtzEsaWaqHDycX7cZGIcx4Sp9XmAgVGboQZpY1QlR60uI4xkNNQQTIxwUaKkEHXp0GWwdrhgYoWyVrTI0A9T+vmLUXKMgIm2HENbTNQOmciwFS2+lWDp5WKFaWc06hYpxs+XzBOVsNVj9MwzR6wLcN1Qw4y3NerRa9TxHTe9lSlmkKcO5fnShiFSX0W/hEhhQyyCEA==); } @font-face { font-family: Xiaolai; src: url(data:font/woff2;base64,d09GMgABAAAAAAQ8ABEAAAAADIgAAAPhAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbHhweBmAANAgKCY4uERAKghSCAgsGAAE2AiQDCAQgBYJeByAMFBtDC8iOw7jhK61Mllg8cBRB/V639y6lq9CK/nlUoXFdli4cg0JabNRXEo/Q6Bz/XjZZo+BLQS5KpfLWuu2dc7ByzslfoP/g/fs5jczLLxkJbFx/L+9S42sMIY/QojCrkrPTBHaK5ayen5DLqF6lwcq/X4nABBAoQgiM7LKBXejS8oCnAysTDFydeR2bB2yKgvhMlC0EBW9FCQ5kuABWYcvVvb4Q4pbv6Ryzw9QBonvAvEoWtvbZPYDSZfzH4ZYusjMxxxSJEssh6LDRXr43mXDmemv1ctfc+5EtQL2kQXRXtA/D5hA0ZppCg2bvKIjkW7k3DVhFSGvQUFilNw2MSgPW4N40MCvSUUxnXU6RI4FIRCbXkkiHGqtqKZZz4mh9UR1J+CIKcu8NiIK8e5yKI6ydHXUEEeFNA2sLwoqTBMygpsWkyUFu88+bBnZNRRTTJasou4gyaqyxX0O34DjSUmeyioLDGmucILxp4FzSkVRGDRbXBW6C05Awb4NALnOQMkGSlbgjDjeQpDXpSFhLaQyoNC5Fw+zVTGctjRmz2ptGR9waxR1xcRvCGic03jTwa2pukVZjjeMEUIBgOQ28z5XyKCGIiFB4MYOW4KU8ZCDnZHIFG0eBJeP/gvemlQgVpQYzohbenEFb4C15WIE1ZyVWG0DbIuwoe0DEXnicQTvgHXk4gTPnJFYXQLsi3Ch38CDuwnsyaC+8Nw8f8OV8xOoHzP3fEUAaKDZhK5MEBXSQ+AC+QBO8QlYbyxLSbInV/oK72Lj5VuFM58VgVC06KCAXgdH2jnox89+TiYtAB5pNt/OqpuUd7jIZu7SuXp1/OXJMLC4TCKbH82Z1OzfwL4XKQ/l33BcnDy3v+oNu70K2+dtOzua1c/KC7V7/mDBhWoNIcSVpk751rlwRJp7yOcfPbxrLw7lHxWBbWOLL+OO8F1Nlu7d5cY16n87javWMcdatXlk0zjYbS/OfMGnK9oOfQhb+EqeLa6JErjcE3cI8fqH8wp3HYgYmu2qWhv2P3jZ33Y2Pb3yqkEZ94wmZ7wH43fjVCYA/T+cFptcm987f7AQwUBa4/zPxzv+A4v/n84LrQiP5AylsVMoncMUkIufS7V7V94gpOnyg8bDQhblLumKm1aVj5NHOsK7rmyLQM98H7hlUadeiikGuRAXqdOlm0K4NEsiHP3+RNOrUqNOqGg6QIF6sFMAvsMOQbvqgR6IuGKjKoL2E+G8Q8QtKAUIaAZRcLIi3agY9lwghfrYIHsiBpQa9E0/tWubgqnYdBk0NGjTqObr9opciaYroFWJyba06+Km34LaQYAcqECRhAQAAAA==); } @font-face { font-family: Xiaolai; src: url(data:font/woff2;base64,d09GMgABAAAAAAPcABEAAAAADBAAAAODAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbHhweBmAANAgKCY4uERAKgRyBIQsGAAE2AiQDCAQgBYJeByAMFBviCsieg3HzcaR6hDg+8REeiGxd22NrRzw8LW3vz0yuNFWbS/a67VVeR9g9h0RYhAYbHyXRIDR6/U9d+bTeIwigbyZfB4heB8k6Ym8AqFOY2lwqXSqFsGQowx3B4M293j2AgZXFidf/5P6F/+/Uw/xcp9jYYr2+/C7RCu1JYwONCTSBSlgNdy3gYJUr3ohih7FLHMZXIjACCCQhBIbMi8HO7AkzEKxA0wAj+3O+y3aBIkmIOtX1cEZwX4pA5PAAHkGiu+fRs1Hd/dH9kev/NeQau1q9cfLNE0gtzz8sC0BCxjhDQHuvZVC46HV/NFhl2uDmtZZx/yN7QHWrIT45/YztEJr9n8a1cJYof2vO0GBF5HMtxVozNFRpsM0zNFyRrYwEe5oTJ0guy+tOSTb1tnUqbwIOdD7VJUUWKfezS8rzzNWRMN25LiFKMzSyB/Xm5QuskH819QfIKX8zNKoaTyonTnGmOmqjZeInLWFyTj51ZYqjrlgmFyJDo28ZSOvr3OH3BaZAt0ma3SExnQVITUhZy8dxOCalKdeE3aoDVFld0hYPGgk+zTZO6e5sjOMmOiWOx3FhcmFlaFw1frrjs4SWiwT/u77BKllGAJEK7pcIWsYrAlTQeFWtc9UMZz9+y3ezBsJkLLCp5X6HoF28J8CHgPflGgI2QsRMAilN3J8RdI4vBJRQ8aVca8A2iJbpoKed+weCHvGTgBkWfpbrChy3NiA79yXTWAMZJFDyxC6DI6AsJ6TlQQvk6SNs01HzydLJeDtYyveezH4S1ncY/4xDMx4O3/d7xjb7AGNHkw6OPnpkf9//OfhG93bYFn/IkFU25MxRfczlwf4MPqpu8NGw8fc9FZcaOmlD2FfYU+0Cz9+T8fZvLXCw/1HtS7bEGDzn8UQdCQQI5PmU/8leWUvU3tfgYeMdwJ9fX+IB/nUS9jRJKX/yF2BAAkGu/9dQ/jf9U/kjeOE5d+Sf+DCk9BFJAOB2aAp5+/q9MkQJAW3PS3MYd8ucjOmZsw1ysYybW74bmOgz0sEzV5sF5mrjmq7OLF0WW8K1wHwkT6YcOcpYunToMk87FCP50pFc2WG1jD1/dqk6i3Vpw7CgSnJ2yGTjaq7CVoLS3fJlaOdaeosU8c1KeWIarPZYNnPt4nUBrl9goVX2rh69li723VSUTOAYbTaD56+pZWv10HYkxGACXwQdAA==); } @font-face { font-family: Xiaolai; src: url(data:font/woff2;base64,d09GMgABAAAAAARgABEAAAAADLAAAAQFAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhYbHhweBmAANAgKCY4uERAKgjyCJAsGAAE2AiQDCAQgBYJeByAMFBtlC8geg+NuKcNiiSWmINp9/h4P/7/2+33unYdYMm1mkUxzD3EioVDJ8E0aq0+JP9K/aP2V+fk68/1MyFX0Z0EGS2Tqhdik9dS6wVNPzZN0uNEFQe7//2UO7p2/hResdLH9e+ZtE12R+Vsr1qKlpWoFCmE0VJrleDpHOeIZYcwwdonD+JUIdAACSQiBNsMM0KqtsBKECqhrGHk45y+xGngkCVGmlgpiQziXokkOD0LC1vqxvQYyHxF9VaL8X4JcwuprLnr79ilIjfSMcTwkZHQjBDJzjaPQxfAx/testL95s6Eb/8gKYHaNBem24yhCdxSL/t+yYBk5A1L5alYYC2gWY2urhUFzwljgzLKApskwFohZbGMIP1u5w4fCNKbzGmhswy21DQzXD4vOFzXSVARmoOK4FTNQeUwymZTe3dNIUclhLHD3YNwVmsI23LaatQnymX/DWODNFmCG8C91lDkYp0XPZFooPUliK3O51MFctuhJigpjgX9LkbYmWz3uC0GVP00Kwh2yFUyQJkXTtYoPCddoWk/7UPZSFgE1ZNZZWLxI+FlZ5Czt7jt9SD16jg/pQ1J6krKEsSCaLahwWC1CpQJmxb8bGYQGISeBlEmCL5PIcrpCgRIqWWlS6zI58Of8W/DdvIag5XRwY7rgu0tkD7qnAi/oZS+tGgBvJJg4MzAzB5+UyN50HwW+8JN9teoP+ABCIBeEYBYU/BCJHEoPUxCOCDlcq5HAN+oTAWQtkn6ormWQQIGMErgFbfiuPBtgrKGR62nBuadPkWujuPJRG73bbSNyuW+7JTn99NZpyTJw7jOPSs+zvqzv/V633VuRSz8tI6ag1bpA3U1Pa56Kmx/rNoCzez08mlw/faOb/wuvTlVs7aiy3vgMzgr/a8J6zuHN2vXNP6nMhUXvnuobI6bJUUvXvTyX686ZU3C7XTARk1KNijiaekGUyc0tMyCjlvK3n/qyvBpVlvoWSDWhwmI3N0ltUvIufnJyncBL5B9qEQ4RC7n5XtM4XXlS9ejtFfGS3q/KXQ/DRFsDQrSdeuNfdIhB73uNvhExAo2hPVY9/ywABPJwkJ0zcu1KkfpVKMm/4OnjO18Avs74rf2/Dir+5F4ALRIIcvm/BMX/+vO/4o/gJZyH8mtwQEjprwmrjmEZJOihZqcIUbRi2ovSGbrddCZdKmc2jz5ZjDsbbqizTeY7wLFWtbp1qNWqQo5qjfr0a9WtCxYjXJQoKSwa1YeNTnVohsUKZY1okUiFedv82YAcfTBbm6O7gUXtUIwMG9HiW1mcXi1WmDqtBm6Rx/xiyTxbDhvNBmee27dOcGu3HqPerZq1GDi576bXYoUc7GoQvGh1KVK3EjgjYYIW4hrMAAAAAA==); } @font-face { font-family: Excalifont; src: url(data:font/woff2;base64,d09GMgABAAAAAAHwAA0AAAAABBAAAAGhAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGx4cNAZgADQRCAoALAsGAAE2AiQDCAQgBYMYByAbUwPIrgpscEcPjAqbbfFGeKx5Kz5EU3R4bDzUL93bvX8JXUVSmorE9SJxvRjNeBQOYXEOHSway/n/P+buVKO/qf2JJVWsT8RCwUokBQ4hUxIh0zx2kOoSixA6//37cw+I33LHb5EcSZbiLwn1l1CELdAXC7/Ia2HUcht7no7PES0JadRRYMAoA87WTvMJYgUaIDBgDsM3u00398MKuofDeAnda/+0gQ7Y6yWfIrW+/EzgUNU4x/xDBFjZOqq08QNClf+f6uoSTsInxFsCk8RIVOjgCel8k6AoApMN3NH0cADRkCBBwaAYFwg+3u9CxZ9YxvsDevfuFsDP16+mlcs86JMgeJ5qrXFd4uBUWQ2UIgTSN4QgEQJrAGg7xhNFAl79NkLHN9KIL1S6XlHMGqC2wm7dMgu23Qz1rcxNbG2cBDwYmzpb6Tt4MXZwNLe1QSKCwvXjrIqVu52ZIxIVxBC3KIZJ8px7SJ61Fz2qaMtGEnnLBb26c3cwNzVzQtyGPAgTxsSQgTtKV44c9F0FkZyVFcrjcfBYND7qYmwkiAc0qxEAAA==); }</style><rect x="0" y="0" width="632.0353021317197" height="468.12524655748945" fill="#ffffff"></rect>버그 심각도코드 단순성<mask></mask><mask></mask>

복잡도를 올리는 방향으로는 점진적으로 빈도와 심각도를 낮출 수 있지만 수렴하는 위치가 최적이 아닌 문제가 있고, 복잡도를 낮추는 방향으로 가면 점진적인 접근을 시도하기 어려워서 문제를 해결하기까지의 비용이 쉽게 예상되지 않는 문제가 있다.

회사에서 복잡도를 높이는 방향으로 문제 해결을 요구받는 일이 많은데 최적해로 가길 원하는 엔지니어로서의 자아와 늘 충돌하게 된다.

개인시간을 희생해서 문제를 푸는 데에도 한계가 있고 (풀어서 갖고와도 도입하는데 많은 설득비용을 써야함).. 그냥 회사에서는 자아실현을 포기해야 하는지 싶음.

Read more →
0
0
0
0
0
0

@xiniha 님께서 Hackers' Pub에 눈에 보이진 않지만 큰 기여를 해 주셨습니다. Drizzle ORM 베타 버전에서 쓸 수 있는 릴레이셔널 API v2Hackers' Pub 코드 전체에 적용하는 큰 패치가 바로 그것입니다.

기능적으로 눈에 바뀌는 것은 없겠지만, 아마 성능상으로는 약간의 개선이 있을 수 있습니다. 기존에는 복잡한 관계 필터를 서브쿼리 방식으로 해 왔는데, 릴레이셔널 API v2를 쓰면 JOIN으로 바뀌는 것 같아요. 물론 PostgreSQL의 쿼리 최적화기가 뛰어나다면 두 방식 중 어떤 방식을 쓰든 같은 실행 계획을 수립할 것이므로 성능 차이가 없을 수도 있지만요. 아니면 더 느려질 수도 있겠죠. 거기까지 세세하게 비교 테스트해보진 않았습니다. 😅

참고로 해당 변경은 이미 배포된 상태입니다. 아무튼 고생해주신 @xiniha 님께 박수 부탁드립니다. 👏

0
0
0
0

XiNiHa shared the below article:

Hacker's Pub에 입문한 한국어권 여러분을 위한 안내서

Jaeyeol Lee @kodingwarrior@hackers.pub

먼저 터를 잡은 사람으로서 Hacker's Pub에 오신 여러분들을 환영합니다. 현재 추정하건데 150명이 넘는 분들께서 Hacker's Pub에 들어오고 계신 것 같은데요. 물이 들어오면.... 노를 젓는게 인지상정이겠죠? 이 서비스가 만들어진 초기부터 관심을 가져왔고, 이 서비스의 가능성을 믿는 사람으로서 여러분에게 안내를 드리고자 합니다.

Hacker's Pub은 무엇을 하는 곳인가요?

Hacker's Pub은 소프트웨어 업계에 몸을 담고 있는 여러분들의 찰나의 생각, 혹은 장문의 정제된 생각들을 자유롭게 개시할 수 있는 소셜 네트워크 서비스이자, 블로깅 플랫폼입니다. 여러분은 그때그때 드는 생각들을 올려서 다른 사람들과 자유롭게 소통이 가능합니다.

Hacker's Pub 이라는 이름은 중의적인 의미를 가지고 있습니다.

  • ActivityPub 프로토콜을 지원하는 해커들을 위한 커뮤니티
  • Hacker's + Pub - 해커들이 자유롭게 웃고 떠들 수 있는 주점

Hacker's Pub이라는 커뮤니티에 애정을 가지고 있는 저로서는, 여기에 들어오시는 모든 분들이 Pub에 들렸다가 가는 것처럼 편안한 공간으로 느껴지시길 바랍니다. 우리 모두가 편안함 경험을 누릴 수 있도록 초대제로 운영이 되고 있습니다. 또한, 사회적 합의로서 행동강령도 마련되어 있으니 한번 참고해보시면 좋을 것 같습니다.

ActivityPub 프로토콜이란 무엇인가요?

Hacker's Pub은 ActivityPub 프로토콜을 지원하는 커뮤니티서비스입니다. 그렇다면, ActivityPub은 무엇일까요? ActivityPub은 웹 상의 분산형 소셜 네트워크를 구현하기 위한 국제 표준 프로토콜로, W3C에서 표준화되었습니다. Mastodon, Misskey, Pleroma 등 다양한 SNS 서비스들이 이 프로토콜을 기반으로 상호 호환되며 자유롭게 소통할 수 있도록 지원합니다. 쉽게 말해, 어떤 SNS를 사용하더라도 경계를 넘어 서로 소통할 수 있도록 돕는 것이 ActivityPub의 핵심입니다.

ActivityPub에 대한 자세한 설명은 여기가 정말 잘 되어 있으니 관심있는 분들은 한번 정독해보시는걸 권장드립니다.

Hacker's Pub을 이용하는 여러분들은 ActivityPub 프로토콜을 사용하는 모든 SNS 서비스(Mastodon, Misskey 등)의 사람들과 연결이 되어 있으며, 심지어 Bluesky에 있는 사람들과 연결이 되어 플랫폼의 경계가 허물어져 있는, 사실상 초연결적인 사회의 경험을 누릴 수 있습니다.

Hacker's Pub 생태계에 기여하기

Hacker's Pub은 아직 Early Stage 단계이며, 여전히 개선하고 발전해야 하는 여지는 있습니다. Hacker's Pub은 모든 것이 오픈소스로 개발되는, 전적으로 개방된 커뮤니티 서비스이기 때문에 여러분도 생태계의 발전에 기여할 수 있습니다. 해커들을 위한 공간이니만큼, 어느 누구에게도 기여는 열려있습니다. 여러분은 여기에서 실시간으로 기여에 참여하실 수 있습니다.

경우에 따라서는 Hacker's Pub에 당장은 기여하기가 어려울 수도 있습니다. ActivityPub 프로토콜에 대한 사전지식이라던가 혹은 서비스가 만들어진 기술스택에 대한 사전지식 같은 것들이 진입장벽이 될 수는 있을 것이거든요. 그렇다고 해도 괜찮습니다. ActivityPub에 대한 이해는 한국 연합우주 개발자 모임에서 함께 커뮤니티 차원에서 함께 알아가면 됩니다.

더불어, 시간이 지나면 Hacker's Pub 주변 생태계도 더더욱 발전할 수 있을 것이라 믿습니다. 당장은 서버와 클라이언트가 합쳐져서 개발되고 있는 형태이지만, 조만간 서버와 클라이언트가 분리되어서 개발될 예정이고, Mastodon/Misskey 같은 다른 ActivityPub 기반의 서비스가 그래왔듯, 우리만의 Hacker's Pub 클라이언트를 만들게 되는 날도 올 것입니다.

Hashnode 처럼 Hacker's Pub 블로그 템플릿 같은 것이 만들어지는 미래, 기대되지 않나요?

함께 만들어가는 Hacker's Pub

Hacker's Pub은 해커들을 위한 공간인 동시에, 모든 분들이 각자의 색깔과 생각을 마음껏 표현할 수 있는 열린 무대입니다. 우리 커뮤니티는 상호 존중과 신뢰를 기반으로 하며, 이를 위해 마련된 행동강령을 준수하는 동시에, 우리 모두의 다양한 의견과 피드백이 존중될 수 있는 공간입니다. 만약 새로운 아이디어나 개선 사항이 있다면 언제든지 의견을 공유해도 됩니다. 우리 모두의 참여가 바로 Hacker's Pub의 미래를 밝게 만드는 원동력입니다.

Read more →
4
0

블루스카이를 연합우주보다 먼저 썼고, 해커뉴스에서 관련 주장에 대해서 꽤 싸우기도 한 입장에서 민희님의 글 〈Bluesky는 X의 훌륭한 대안일 수 있지만, 연합우주의 대안은 아닙니다〉에 대한 반대 의견을 제시하고자 한다. 이 의견이 연합우주에 대한 전면적인 비판이 아니라는 것을 의견을 제시하기에 앞서 확실히 해 둔다(그랬다면 Hackers' Pub에 들어 올 일이 없었겠지).

탈중앙화는 매력적인 개념임이 틀림 없다. 인터넷의 많은 중요한 요소들이 어느 정도 탈중앙화되어 있으므로 탈중앙화가 인터넷의 장점들에 큰 몫을 했다는 생각을 쉬이 할 수 있고, 어느 정도는 그게 사실이기도 하니까. 하지만 엄밀히 말하자면 탈중앙화는 기술적인 특징이지 그 자체로 장점이 아니며, 탈중앙화가 장점으로 작용하려면 연결고리가 필요하다. 이를테면 비트코인을 위시한 암호화폐는 본디 비잔틴 실패까지 대비할 수 있는 강력한 탈중앙화를 장점으로 내세웠으나, 결국 화폐로서 제대로 사용되기 시작하자 현실 경제와의 커플링 때문에 그 "장점"이 크게 희석되고 말았다. 현 시점에서 암호화폐는 무에서 유의 신뢰를 창조하여 신용화폐의 요건을 충족하는 데까지는 성공했고 그것만으로도 역사적인 일이기는 하지만, 그게 탈중앙화랑 무슨 상관이 있느냐 하면 글쎄올시다.

블루스카이가 연합우주보다 덜 탈중앙화되어 있음은 분명하다. 민희님의 글에서 지적되었듯, 블루스카이가 이런 선택을 한 가장 큰 이유는 온전한 소셜 네트워크 기능을 위해 전역 뷰가 필수적으로 필요하다고 보았기 때문이다. 반대로 말하면 연합우주는 더 탈중앙화를 하기 위하여 전역 뷰를 포기했는데, 이 때문에 연합우주에서의 "소셜 네트워크"는 트위터/X와는 구조가 크게 다르다. 노드 규모가 문턱값에 다다르지 못하면 다른 노드에 있는 사용자를 찾아서 팔로해야만 온전한 소셜 네트워크 구성이 가능한데, 연합우주 안에서는 이런 외부 사용자를 찾는 구체적인 방법을 제공하지 않는다. 물론 인터넷과 똑같이 검색 엔진이 존재할 수야 있겠지만, 크롤링으로 인한 부하와 프라이버시에 대한 의견 차이 때문에 현실적으로 작동하는 연합우주 내 검색 엔진은 없다고 알고 있다. 따라서 연합우주에서 소셜 네트워크의 구성은 연합우주 바깥의, 보통은 중앙화되어 있는, 다른 소셜 네트워크(이를테면 현실 인간 관계)를 빌어야만 하는데, 이러면 탈중앙화가 큰 가치가 있을까?

한편으로는 전역 뷰가 소셜 네트워크의 단점이라고 주장할 수 있는 여지도 있다. 트위터/X를 오래 써 본 사람이라면 다 알겠지만 한 무리의 사람들이 다른 의견을 가진 무리와 충돌하는 주된 통로는 검색이나 해시태그를 통한 노출, 즉 전역 뷰이기 때문이다. 그러나 현실의 규모 있는 연합우주 노드들을 살펴 보면 각 노드가 곧 한 무리에 대응하는 식으로 충돌을 미리 회피하는 형태로 구성되지, 딱히 이런 충돌을 막기 위한 접근을 가지고 있는 것은 아니다. 노드 운영자를 위해 차단하는 걸 추천하는 서버 목록 같은 게 돌아다니는 건 연합우주 바깥의 일이지 않는가. 결국 전역 뷰의 역할을 대체하는 소셜 네트워크 바깥의 또 다른 소셜 네트워크가 존재할 것이기에, 우리가 소셜 네트워크를 어떤 이유로든 유용하다고 여긴다면 전역 뷰가 없는 게 장점이 될 수는 없다.

모든 이들이 이런 사고 과정을 가지고 블루스카이나 연합우주를 선택했다고 생각하진 않지만, 적어도 현 시점에서 사용자들은 블루스카이(이 글을 쓰는 시점에서 약 3360만명)를 연합우주(FediDBFediverse.party로부터 추정할 때 최대 1530만명)보다 선호하는 것은 틀림이 없다. 게다가 블루스카이의 규모는 최근 1년 사이에 10배 불어난 것이고, 조금 장애가 있었지만 현재는 잘 동작하는 것으로 보인다. 위의 논의와 결합해 보면, 블루스카이는 정석적인 스케일링에 성공하고 있는 반면 연합우주는 스케일링 문제를 회피하기 위해 온전한 소셜 네트워크의 구성을 포기했다고 볼 수도 있는 대목이다. 블루스카이가 못미더운 부분은 분명히 존재하지만, 연합우주가 더 좋은 소셜 네트워크 경험을 제공한다고 가정하고 블루스카이의 단점을 제시할 수는 없다. 마치 암호화폐를 논할 때 장점만 말할 수 없는 것과 마찬가지로 말이다.



RE: https://hackers.pub/@hongminhee/2025/bluesky-a-good-alternative-to-x-not-to-the-fediverse

0

지금 해커스펍은 Fresh를 활용한 MPA 앱으로 구현되어 있는데, 개인적으로 이것 때문에 이런저런 사용성 아쉬움을 느끼고 있었다. 그래서 해커스펍에 GraphQL API를 붙여서 SPA 프론트엔드를 새로 구현하겠다는 음모계획을 가지고, 이를 위한 기반 작업의 일환으로 Drizzle의 새로운 Relational Query Builder API(RQBv2)를 적용하는 PR을 만들어 보았다 😋

0

Hackers' Pub에 드디어 인용 기능이 구현되었습니다. 인용할 글의 링크를 복사한 뒤 단문 작성창에 붙여넣으시면 해당 글을 인용할지 묻는 창이 뜹니다. 확인을 선택하시면 해당 글이 인용되게 됩니다.

참고로 인용할 글은 꼭 Hackers' Pub의 글이 아니어도 ActivityPub을 지원하는 사이트의 아무 글이나 다 가능합니다. 예를 들어 Mastodon 인스턴스에서 글 링크를 복사해서 붙여도 동작합니다.

내가 쓴 글에 누가 어떻게 인용을 했나 궁금하실 경우, 글 아래에 있는 공유 아이콘 오른쪽에 위치한 반응 아이콘을 누르시면 확인할 수 있습니다. (원래는 공유한 사람 탭만 있었는데 인용 탭이 새로 생겼습니다.)

기술적으로는 FEP-e232 오브젝트 링크 스펙과 Misskey의 인용 확장 스펙, Pleroma의 인용 확장 스펙, 그리고 Fedibird의 인용 확장 스펙을 모두 구현하기 때문에, 인용 기능을 지원하는 현존하는 모든 ActivityPub 서비스와 호환됩니다.



RE: https://hackers.pub/@hongminhee/0195c73c-24f5-74c0-883d-1a0a0db14b6d

Hackers' Pub의 단문 작성창에 인용할 다른 글의 링크를 복사하여 붙여넣는 모습. 붙이고 나면 해당 글을 인용할지 묻는 창이 뜨고, 확인을 선택하면 해당 글이 인용된다.
0

Hackers' Pub에 행동 강령이 있다는 사실, 아셨나요?

우리 커뮤니티는 단순한 기술 토론을 넘어 모든 구성원이 진정으로 환영받는 포용적인 공간을 만들기 위해 상세한 행동 강령을 마련했습니다.

특히 주목할 만한 점은:

  1. 구조적 차별에 대한 명확한 입장: “모든 사람을 동등하게 대우한다”는 명목 하에 현실의 구조적 불평등을 무시하지 않으며, 이를 극복하기 위한 적극적인 노력을 중요시합니다.

  2. 기술적 엘리트주의 지양: “이것도 모르세요?”와 같은 조롱, 특정 기술 스택이나 도구에 대한 비하, 초보자의 질문을 무시하는 행위를 명확히 금지합니다.

  3. 모든 언어의 동등한 존중: 전 세계의 모든 언어를 동등하게 존중하며, 어떤 언어로도 자유롭게 소통할 수 있습니다.

자세한 내용은 행동 강령 페이지에서 확인하실 수 있습니다.

0
0

며칠 전 Fedify에 팬아웃을 두 단계로 나누는 변경을 통해 Hackers' Pub에서 단문 작성시 오래 걸리는 문제를 해결했었는데 (그래봤자 팔로워가 100명이 넘는 나한테나 느낄 수 있는 문제였을 것 같지만), 이렇게 하니까 큐에서 팬아웃 태스크 자체가 오랫동안 안 빠지는 체증이 존재해서 큐에 여러 메시지를 넣는 연산 자체를 새로 추가하고 있다. 정확히는 PostgreSQL을 큐로 사용하고 있는데, 메시지 하나 넣고 NOTIFY하고, 다음 메시지 넣고 또 NOTIFY하고… 하는 게 비효율적이라 메시지를 일단 다 넣은 다음 NOTIFY를 한 번만 하도록 고치고 있다.

0

XiNiHa shared the below article:

Bluesky는 X의 훌륭한 대안일 수 있지만, 연합우주의 대안은 아닙니다

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

최근 X(구 Twitter)를 떠나는 사람들이 늘면서 Bluesky에 대한 관심이 뜨겁습니다. Bluesky는 깔끔한 인터페이스와 과거 Twitter와 유사한 사용자 경험을 제공하며, 신뢰할 수 있는 이탈(credible exit)이라는 매력적인 개념을 내세워 X의 유력한 대안으로 떠오르고 있습니다. 하지만 Bluesky와 그 기반 프로토콜인 AT Protocol을 연합우주(fediverse)의 대안으로 보기에는 근본적인 차이가 존재합니다. 이 글에서는 Christine Lemmer-Webber 씨(@cwebber)의 날카로운 분석(〈Bluesky는 실제로 얼마나 탈중앙화 되어 있나〉 및 〈답장: 답장: Bluesky와 탈중앙화〉)을 바탕으로, Bryan Newbold 씨(@bnewbold)의 반론(〈Bluesky와 탈중앙화에 대한 답변〉)을 충분히 고려하면서 Bluesky가 어째서 X의 대안은 될 수 있어도 연합우주의 대안은 될 수 없는지 이야기를 풀어볼까 합니다.

메시지 전달 對 공유 힙: 근본적인 설계 차이

Bluesky와 연합우주의 가장 큰 차이점 중 하나는 설계입니다. 연합우주는 이메일이나 XMPP와 유사한 메시지 전달(message passing) 방식을 채택하고 있습니다. 이는 특정 수신자에게 메시지를 직접 전달하는 방식으로, 효율성이 높습니다. 예를 들어, 수많은 서버 중 단 몇 곳의 사용자만 특정 메시지에 관심을 있다면 해당 서버에만 메시지를 전달하면 됩니다. 비유하자면, 철수가 영희에게 편지를 보내려면 직접 영희의 집으로 편지를 보내고, 영희가 회신하고 싶으면 직접 철수에게 회신하는 것과 같은 방식입니다.

반면, Bluesky는 공유 힙(shared heap) 방식을 사용합니다. 이는 메시지를 특정 수신자에게 직접 보내는 대신, 모든 메시지를 중앙의 “릴레이”라는 곳에 저장하고, 관심 있는 사용자가 릴레이에서 자신에게 필요한 정보를 필터링하는 방식입니다. 이는 마치 모든 편지가 하나의 거대한 우체국(릴레이)에 쌓이고, 각자가 이 우체국에 방문하여 자신에게 관련된 편지를 직접 찾아야 하는 것과 같습니다. 이런 방식에서는 메시지가 직접 전달되지 않기 때문에, 답글이 어떤 메시지에 대한 것인지 파악하려면 모든 가능한 메시지를 알고 있어야 합니다.

이 설계는 데이터와 색인을 분리하여 유연성을 제공한다는 주장도 있지만, 필연적으로 대규모 중앙 집권화된 릴레이에 의존하게 되어 탈중앙화의 이상과는 거리가 멀어진다는 한계가 있습니다.

결국 Bluesky가 공유 힙 방식을 채택하고 중앙 집권화된 릴레이에 의존하게 되는 데에는 운영 비용이라는 현실적인 이유가 크게 작용합니다. Christine Lemmer-Webber 씨의 분석에 따르면, Bluesky에서 전체 네트워크 기록을 저장하는 릴레이를 운영하는 데에는 상당한 스토리지를 요구하며, 이는 빠르게 증가하고 있습니다. 2024년 7월에는 약 1TB의 저장 공간이 필요했지만, 불과 4개월 후인 11월에는 약 5TB로 증가했습니다. 상업용 호스팅 서비스 기준으로 이는 연간 수만 달러(약 $55,000)에 달하는 비용이 발생할 수 있습니다.

반면, 연합우주에서는 개인이나 소규모 단체가 Raspberry Pi와 같은 저렴한 장비로도 GoToSocial과 같은 소프트웨어를 실행하여 독립적인 노드를 운영할 수 있습니다. 물론 대규모 연합우주 인스턴스는 더 많은 비용이 들겠지만, Bluesky의 전체 릴레이 운영 비용과는 비교하기 어려울 정도로 저렴합니다. 이처럼 운영 비용의 현격한 차이는 Bluesky가 분산된 구조를 채택하기 어렵게 만들고, 결국 중앙 집권화된 릴레이에 의존하게 만드는 주요 원인이라고 볼 수 있습니다.

전역 뷰에 대한 집착과 중앙 집권화의 심화

Bluesky는 댓글 누락과 같은 문제를 피하기 위해 네트워크 전체의 일관된 전역 뷰를 유지하는 데 집중하는 것으로 보입니다. 이러한 목표는 사용자 경험 측면에서 긍정적일 수 있지만, 필연적으로 중앙 집권화를 야기합니다. 대표적인 예가 차단 목록의 전체 공개입니다. 네트워크 전체의 일관성을 유지하기 위해 누가 누구를 차단했는지 모든 앱뷰가 알아야 하므로, 차단 정보가 공개되는 것입니다.

이는 개인 정보 보호 측면에서 심각한 우려를 낳을 수 있습니다. 단순히 누군가의 게시물을 보고 차단된 사람을 추측하는 것과, 네트워크에 “J. K. Rowling[1]을 차단한 모든 사람”을 직접 질의할 수 있는 것 사이에는 큰 차이가 있습니다. 실제로 ActivityPub 개발 과정에서는 이런 문제를 고려하여 서버 간에 차단 활동을 전달하지 않도록 명시적으로 설계했습니다. 이는 차단한 사람이 차단당한 사람의 보복을 받을 위험을 줄이기 위함입니다.

반면 연합우주에서는 각 서버가 독립적으로 차단 정책을 시행하며, 사용자에게 더 많은 자율성을 제공합니다.

AT Protocol과 개방형 표준으로서의 ActivityPub

연합우주의 핵심 프로토콜인 ActivityPub은 W3C의 채택 권고안으로, 개방형 표준입니다. 이는 누구나 자유롭게 구현하고 사용할 수 있으며, 다양한 소프트웨어 간의 상호 운용성을 보장합니다. 현재 페디버스 커뮤니티는 FEP를 중심으로 활발하게 프로토콜을 개선하고 발전시켜 나가고 있습니다. 반면, Bluesky의 AT Protocol은 아직 특정 사기업에 의해 주도되고 있으며, 개방형 표준으로서의 지위는 아직 확립되지 않았습니다. 이는 페디버스가 가진 확장성과 지속 가능성 측면에서 중요한 차이점이라고 할 수 있습니다.

DM의 중앙화

Bluesky는 콘텐츠 주소 지정이나 이동 가능한 아이덴티티와 같은 탈중앙화 요소를 도입했지만, DM은 완전히 중앙화되어 있습니다. 사용자가 어떤 PDS를 사용하든, 어떤 릴레이를 사용하든 상관없이 모든 DM은 Bluesky 회사를 통해 전송됩니다.

이는 Bluesky가 아직 기능적으로 완전한 Twitter 대체품이 되기 위해 속도를 우선시했다는 증거입니다. Bluesky는 이 DM 시스템이 장기적인 솔루션이 아니라고 밝혔지만, 대부분의 사용자들은 이 사실을 인지하지 못하고 있으며 DM도 AT Protocol의 다른 기능처럼 작동한다고 가정합니다.

이러한 중앙화된 DM 구현은 “신뢰할 수 있는 이탈”이라는 Bluesky의 핵심 가치와도 모순됩니다. 만약 Bluesky社가 적대적인 인수나 정책 변경을 겪게 된다면, 사용자들의 개인 대화는 완전히 회사의 통제 하에 남게 됩니다.

이동 가능한 아이덴티티와 DID: Bluesky 방식의 한계

Bluesky는 이동 가능한 아이덴티티(portable identity)를 핵심적인 장점 중 하나로 내세우며, 이를 위해 DIDs, 즉 분산 식별자를 활용합니다. 이는 사용자가 자신의 계정과 데이터를 다른 플랫폼으로 쉽게 이동할 수 있도록 하는 중요한 기능입니다. 하지만 Christine Lemmer-Webber는 AT Protocol이 채택한 did:webdid:plc 방식이 여전히 DNS와 Bluesky社가 관리하는 중앙 집권화된 PLC 레지스트리에 의존하고 있어 완전한 사용자 통제하의 독립적인 아이덴티티를 제공하는지 의문을 제기합니다.

더 놀라운 점은 Bluesky社가 초기에 모든 계정에 대해 동일한 rotationKeys를 사용했다는 사실입니다. 이는 클라우드 HSM 제품이 키별로 비용을 청구해서 각 사용자에게 고유한 키를 제공하는 것이 금전적으로 비용이 많이 들었기 때문이라고 합니다. 이러한 접근 방식은 DIDs 시스템을 구축하는 근본적인 목표와 모순되는 것으로 보입니다.

중요한 점은 DIDs 기술 자체가 탈중앙화된 아이덴티티를 위한 잠재력을 가지고 있음에도, Bluesky와 AT Protocol이 채택한 특정 방식이 중앙 집권화된 요소에 의존한다는 것입니다. 블록체인 기반의 DIDs와 같은 진정으로 탈중앙화된 방식도 존재하지만, AT Protocol은 비교적 구현이 쉬운 did:webdid:plc를 선택했습니다. 따라서 사용자가 Bluesky 생태계를 벗어나 자신의 아이덴티티를 완전히 독립적으로 관리하고자 할 때 제약이 발생할 수 있습니다.

또한 현재 시스템에서는 Bluesky社가 사용자의 키를 대신 관리하고 있어, 사용자가 현재는 Bluesky社를 신뢰하더라도 미래에 신뢰하지 않게 된 경우에도 여전히 회사에 의존해야 합니다. Bluesky社가 사용자를 대신하여 이동을 수행하도록 신뢰해야 하며, 심지어 Bluesky社가 사용자에게 향후 신원 정보를 제어할 권한을 위임하더라도 Bluesky社는 항상 해당 사용자의 키를 통제할 것입니다.

한편, 연합우주에서는 이미 노마딕 아이덴티티(nomadic identity)라는 개념을 통해 이동 가능한 아이덴티티에 대한 논의와 연구가 활발하게 진행되어 왔습니다. 이는 단순히 계정을 이전하는 것을 넘어, 사용자의 데이터와 관계, 심지어 평판까지도 자유롭게 이동할 수 있도록 하는 더 포괄적인 개념입니다. 《We Distribute》에 실린 기사 〈오, Zot! ActivityPub에 노마딕 아이덴티티가 도입된다〉에 소개된 Zot 프로토콜과 같은 기술은 이미 연합우주 안에서 이러한 노마딕 아이덴티티를 구현하기 위한 메커니즘을 제공하고 있습니다. 또한, FEP-ef61와 같은 제안을 통해 ActivityPub 자체를 개선하여 더 나은 이동 가능한 아이덴티티 기능을 추가하려는 노력도 진행 중입니다.

그래서, 결론은?

결론적으로, Bluesky는 사용자 친화적인 인터페이스와 신뢰할 수 있는 이탈 기능을 통해 X의 훌륭한 대안이 될 수 있습니다. Bluesky는 콘텐츠 주소 지정 방식을 통해 노드가 다운되더라도 콘텐츠가 살아남을 수 있게 하는 등 연합우주가 아직 충분히 활용하지 못하는 몇 가지 강점도 가지고 있습니다.

하지만 중앙 집권화된 설계, 전역 뷰에 대한 집착으로 인한 부작용, 개방형 표준으로서의 한계, DM의 중앙화, 그리고 이동 가능한 아이덴티티 구현의 제한점 등 여러 측면에서 연합우주의 대안으로 보기는 어렵습니다. 연합우주는 메시지 전달 방식의 분산된 아키텍처, 낮은 참여 장벽, 개방형 표준 기반의 활발한 커뮤니티 개발, 그리고 사용자에게 더 많은 자율성과 통제권을 제공하는 철학을 바탕으로 구축된, 근본적으로 다른 종류의 탈중앙화 소셜 네트워크입니다.

또한, Bluesky社가 벤처 캐피털 자금을 확보함에 따라 “조직은 미래의 적이다”라는 그들의 자체 인식에도 불구하고, 투자자 수익과 플랫폼 성장이라는 상업적 압력이 진정한 탈중앙화 추구보다 우선시될 위험이 있습니다. 특히 유료 계정과 광고가 도입되면서 이러한 우려는 더욱 커질 수 있습니다.

따라서 Bluesky는 X를 대체할 수 있을지 모르지만, 연합우주가 제공하는 탈중앙화된 가치와 경험을 대체하기는 어려울 것이라고 생각합니다. 두 시스템은 근본적으로 다른 목표와 설계 철학을 가지고 있으며, 이상적으로는 서로를 보완하는 방향으로 발전해 나갈 수 있을 것입니다.


  1. 판타지 소설 시리즈 《해리 포터》의 작가. ↩︎

Read more →
0

📢 Hackers' Pub 초대 시스템 오픈!

Hackers' Pub에 초대 시스템이 적용되었습니다. 이제 설정초대 페이지에서 지인들을 초대할 수 있습니다.

주요 내용:

  • 초대장 3장 지급: 기존 회원분들께 3장의 초대장이 지급되었습니다.
  • 초대 방법: 설정 → 초대 페이지에서 초대 링크를 생성하여 공유하거나, 이메일 주소를 입력하여 초대할 수 있습니다.
  • 추가 초대: 초대장은 향후 비정기적으로 추가될 예정입니다.
  • 자동 팔로: 초대자와 피초대자는 자동으로 상호 팔로됩니다. (언팔로 가능.)

Hackers' Pub의 퀄리티를 유지하고, 더욱 풍성한 기술 논의를 위해 신중한 초대를 부탁드립니다.

궁금한 점이나 건의사항은 답글로 남겨주세요.

Hackers' Pub 커뮤니티 성장에 많은 참여 부탁드립니다!

Hackers' Pub 웹사이트의 설정 메뉴에서 “초대 (3)”이 선택된 화면입니다. 페이지 제목은 “Hackers' Pub에 친구를 초대하세요. 현재 3장의 초대장이 남아 있습니다”로 표시되어 있습니다. 아래에는 이메일 주소를 입력하는 필드와 “이메일 주소는 초대장을 받을 때 뿐만 아니라, 계정에 로그인 할 때도 사용됩니다”라는 안내 문구가 있습니다. 그 아래에는 “추가 메시지”라는 제목의 텍스트 영역과 “초대장을 받는 친구가 볼 수 있는 메시지입니다”라는 설명이 있습니다. 하단에는 “초대장 보내기” 버튼과 “초대한 사람” 목록이 표시되어 있으며, “洪 民意 (Hong Minhee) (@hongminhee@hackers.pub)”라는 이름과 아이디가 적혀 있습니다.
0
0

XiNiHa shared the below article:

Revisiting Java's Checked Exceptions: An Underappreciated Type Safety Feature

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

Despite their bad reputation in the Java community, checked exceptions provide superior type safety comparable to Rust's Result<T, E> or Haskell's Either a b—we've been dismissing one of Java's best features all along.

Introduction

Few features in Java have been as consistently criticized as checked exceptions. Modern Java libraries and frameworks often go to great lengths to avoid them. Newer JVM languages like Kotlin have abandoned them entirely. Many experienced Java developers consider them a design mistake.

But what if this conventional wisdom is wrong? What if checked exceptions represent one of Java's most forward-thinking features?

In this post, I'll argue that Java's checked exceptions were ahead of their time, offering many of the same type safety benefits that are now celebrated in languages like Rust and Haskell. Rather than abandoning this feature, we should consider how to improve it to work better with modern Java's features.

Understanding Java's Exception Handling Model

To set the stage, let's review how Java's exception system works:

  • Unchecked exceptions (subclasses of RuntimeException or Error): These don't need to be declared or caught. They typically represent programming errors (NullPointerException, IndexOutOfBoundsException) or unrecoverable conditions (OutOfMemoryError).

  • Checked exceptions (subclasses of Exception but not RuntimeException): These must either be caught with try/catch blocks or declared in the method signature with throws. They represent recoverable conditions that are outside the normal flow of execution (IOException, SQLException).

Here's how this works in practice:

// Checked exception - compiler forces you to handle or declare it
public void readFile(String path) throws IOException {
    Files.readAllLines(Path.of(path));
}

// Unchecked exception - no compiler enforcement
public void processArray(int[] array) {
    int value = array[array.length + 1]; // May throw ArrayIndexOutOfBoundsException
}

The Type Safety Argument for Checked Exceptions

At their core, checked exceptions are a way of encoding potential failure modes into the type system via method signatures. This makes certain failure cases part of the API contract, forcing client code to explicitly handle these cases.

Consider this method signature:

public byte[] readFileContents(String filePath) throws IOException

The throws IOException clause tells us something critical: this method might fail in ways related to IO operations. The compiler ensures you can't simply ignore this fact. You must either:

  1. Handle the exception with a try-catch block
  2. Propagate it by declaring it in your own method signature

This type-level representation of potential failures aligns perfectly with principles of modern type-safe programming.

Automatic Propagation: A Hidden Advantage

One often overlooked advantage of Java's checked exceptions is their automatic propagation. Once you declare a method as throws IOException, any exception that occurs is automatically propagated to the caller without additional syntax.

Compare this with Rust, where you must use the ? operator every time you call a function that returns a Result:

// Rust requires explicit propagation with ? for each call
fn read_and_process(path: &str) -> Result<(), std::io::Error> {
    let content = std::fs::read_to_string(path)?;
    process_content(&content)?;
    Ok(())
}

// Java automatically propagates exceptions once declared
void readAndProcess(String path) throws IOException {
    String content = Files.readString(Path.of(path));
    processContent(content); // If this throws IOException, it's automatically propagated
}

In complex methods with many potential failure points, Java's approach leads to cleaner code by eliminating the need for repetitive error propagation markers.

Modern Parallels: Result Types in Rust and Haskell

The approach of encoding failure possibilities in the type system has been adopted by many modern languages, most notably Rust with its Result<T, E> type and Haskell with its Either a b type.

In Rust:

fn read_file_contents(file_path: &str) -> Result<Vec<u8>, std::io::Error> {
    std::fs::read(file_path)
}

When calling this function, you can't just ignore the potential for errors—you need to handle both the success case and the error case, often using the ? operator or pattern matching.

In Haskell:

readFileContents :: FilePath -> IO (Either IOException ByteString)
readFileContents path = try $ BS.readFile path

Again, the caller must explicitly deal with both possible outcomes.

This is fundamentally the same insight that motivated Java's checked exceptions: make failure handling explicit in the type system.

Valid Criticisms of Checked Exceptions

If checked exceptions are conceptually similar to these widely-praised error handling mechanisms, why have they fallen out of favor? There are several legitimate criticisms:

1. Excessive Boilerplate in the Call Chain

The most common complaint is the boilerplate required when propagating exceptions up the call stack:

void methodA() throws IOException {
    methodB();
}

void methodB() throws IOException {
    methodC();
}

void methodC() throws IOException {
    // Actual code that might throw IOException
}

Every method in the chain must declare the same exception, creating repetitive code. While automatic propagation works well within a method, the explicit declaration in method signatures creates overhead.

2. Poor Integration with Functional Programming

Java 8 introduced lambdas and streams, but checked exceptions don't play well with them:

// Won't compile because map doesn't expect functions that throw checked exceptions
List<String> fileContents = filePaths.stream()
    .map(path -> Files.readString(Path.of(path))) // Throws IOException
    .collect(Collectors.toList());

This forces developers to use awkward workarounds:

List<String> fileContents = filePaths.stream()
    .map(path -> {
        try {
            return Files.readString(Path.of(path));
        } catch (IOException e) {
            throw new UncheckedIOException(e); // Wrap in an unchecked exception
        }
    })
    .collect(Collectors.toList());

3. Interface Evolution Problems

Adding a checked exception to an existing method breaks all implementing classes and calling code. This makes evolving interfaces over time difficult, especially for widely-used libraries and frameworks.

4. Catch-and-Ignore Anti-Pattern

The strictness of checked exceptions can lead to the worst possible outcome—developers simply catching and ignoring exceptions to make the compiler happy:

try {
    // Code that might throw
} catch (Exception e) {
    // Do nothing or just log
}

This is worse than having no exception checking at all because it provides a false sense of security.

Improving Checked Exceptions Without Abandoning Them

Rather than abandoning checked exceptions entirely, Java could enhance the existing system to address these legitimate concerns. Here are some potential improvements that preserve the type safety benefits while addressing the practical problems:

1. Allow lambdas to declare checked exceptions

One of the biggest pain points with checked exceptions today is their incompatibility with functional interfaces. Consider how much cleaner this would be:

// Current approach - forced to handle or wrap exceptions inline
List<String> contents = filePaths.stream()
    .map(path -> {
        try {
            return Files.readString(Path.of(path));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    })
    .collect(Collectors.toList());

// Potential future approach - lambdas can declare exceptions
List<String> contents = filePaths.stream()
    .map((String path) throws IOException -> Files.readString(Path.of(path)))
    .collect(Collectors.toList());

This would require updating functional interfaces to support exception declarations:

@FunctionalInterface
public interface Function<T, R, E extends Exception> {
    R apply(T t) throws E;
}

2. Generic exception types in throws clauses

Another powerful enhancement would be allowing generic type parameters in throws clauses:

public <E extends Exception> void processWithException(Supplier<Void, E> supplier) throws E {
    supplier.get();
}

This would enable much more flexible composition of methods that work with different exception types, bringing some of the flexibility of Rust's Result<T, E> to Java's existing exception system.

3. Better support for exception handling in functional contexts

Unlike Rust which requires the ? operator for error propagation, Java already automatically propagates checked exceptions when declared in the method signature. What Java needs instead is better support for checked exceptions in functional contexts:

// Current approach for handling exceptions in streams
List<String> contents = filePaths.stream()
    .map(path -> {
        try {
            return Files.readString(Path.of(path));
        } catch (IOException e) {
            throw new RuntimeException(e); // Lose type information
        }
    })
    .collect(Collectors.toList());

// Hypothetical improved API
List<String> contents = filePaths.stream()
    .mapThrowing(path -> Files.readString(Path.of(path))) // Preserves checked exception
    .onException(IOException.class, e -> logError(e))
    .collect(Collectors.toList());

4. Integration with Optional<T> and Stream<T> APIs

The standard library could be enhanced to better support operations that might throw checked exceptions:

// Hypothetical API
Optional<String> content = Optional.ofThrowable(() -> Files.readString(Path.of("file.txt")));
content.ifPresentOrElse(
    this::processContent,
    exception -> log.error("Failed to read file", exception)
);

Comparison with Other Languages' Approaches

It's worth examining how other languages have addressed the error handling problem:

Rust's Result<T, E> and ? operator

Rust's approach using Result<T, E> and the ? operator shows how propagation can be made concise while keeping the type safety benefits. The ? operator automatically unwraps a successful result or returns the error to the caller, making propagation more elegant.

However, Rust's approach requires explicit propagation at each step, which can be more verbose than Java's automatic propagation in certain scenarios.

Kotlin's Approach

Kotlin made all exceptions unchecked but provides functional constructs like runCatching that bring back some type safety in a more modern way:

val result = runCatching {
    Files.readString(Path.of("file.txt"))
}

result.fold(
    onSuccess = { content -> processContent(content) },
    onFailure = { exception -> log.error("Failed to read file", exception) }
)

This approach works well with Kotlin's functional programming paradigm but lacks compile-time enforcement.

Scala's Try[T], Either[A, B], and Effect Systems

Scala offers Try[T], Either[A, B], and various effect systems that encode errors in the type system while integrating well with functional programming:

import scala.util.Try

val fileContent: Try[String] = Try {
  Source.fromFile("file.txt").mkString
}

fileContent match {
  case Success(content) => processContent(content)
  case Failure(exception) => log.error("Failed to read file", exception)
}

This approach preserves type safety while fitting well with Scala's functional paradigm.

Conclusion

Java's checked exceptions were a pioneering attempt to bring type safety to error handling. While the implementation has shortcomings, the core concept aligns with modern type-safe approaches to error handling in languages like Rust and Haskell.

Copying Rust's Result<T, E> might seem like the obvious solution, but it would represent a radical departure from Java's established paradigms. Instead, targeted enhancements to the existing checked exceptions system—like allowing lambdas to declare exceptions and supporting generic exception types—could preserve Java's unique approach while addressing its practical limitations.

The beauty of such improvements is that they'd maintain backward compatibility while making checked exceptions work seamlessly with modern Java features like lambdas and streams. They would acknowledge that the core concept of checked exceptions was sound—the problem was in the implementation details and their interaction with newer language features.

So rather than abandoning checked exceptions entirely, perhaps we should recognize them as a forward-thinking feature that was implemented before its time. As Java continues to evolve, we have an opportunity to refine this system rather than replace it.

In the meantime, next time you're tempted to disparage checked exceptions, remember: they're not just an annoying Java quirk—they're an early attempt at the same type safety paradigm that newer languages now implement with much celebration.

What do you think? Could these improvements make checked exceptions viable for modern Java development? Or is it too late to salvage this controversial feature? I'm interested in hearing your thoughts in the comments.

Read more →
0

C++ 표준화 위원회(WG21)에게 C++의 원 저자인 비야네 스트롭스트룹Bjarne Stroustrup이 보낸 메일이 이번 달 초에 본인에 의해 공개된 모양이다. C++가 요즘 안전하지 않은 언어라고 열심히 얻어 맞고 있는 게 싫은지 프로파일(P3081)이라고 하는 언어 부분집합을 정의하려고 했는데, 프로파일이 다루는 문제들이 아주 쉬운 것부터 연구가 필요한 것까지 한데 뒤섞여 있어 구현이 매우 까다롭기에 해당 제안이 적절하지 않음을 올해 초에 가멸차게 까는 글(P3586)이 올라 오자 거기에 대한 응답으로 작성된 것으로 보인다. 더 레지스터의 표현을 빌면 "(본지가 아는 한) 스트롭스트룹이 이 정도로 강조해서 말하는 건 2018년 이래 처음"이라나.

여론은 당연히 호의적이지 않은데, 기술적인 반론이 대부분인 P3586과는 달리 해당 메일은 원래 공개 목적이 아니었음을 감안해도 기술적인 얘기는 쏙 빼 놓고 프로파일이 "코드를 안 고치고도 안전성을 가져 갈 수 있다"는 허황된 주장에 기반해 그러니까 프로파일을 당장 집어 넣어야 한다고 주장하고 있으니 그럴 만도 하다. 스트롭스트룹이 그렇게 이름을 언급하지 않으려고 했던 러스트를 굳이 들지 않아도, 애당초 (이 또한 계속 부정하고 싶겠지만) C++의 주요 장점 중 하나였던 강력한 C 호환성이 곧 메모리 안전성의 가장 큰 적이기 때문에 프로파일이 아니라 프로파일 할아버지가 와도 안전성을 진짜로 확보하려면 코드 수정이 필수적이고, 프로파일이 그 문제를 해결한다고 주장하는 건 눈 가리고 아웅이라는 것을 이제는 충분히 많은 사람들이 깨닫지 않았는가. 스트롭스트룹이 허황된 주장을 계속 반복하는 한 C++는 안전해질 기회가 없을 듯 하다.

0
1

Got an interesting question today about 's outgoing design!

Some users noticed we create separate queue messages for each recipient inbox rather than queuing a single message and handling the splitting later. There's a good reason for this approach.

In the , server response times vary dramatically—some respond quickly, others slowly, and some might be temporarily down. If we processed deliveries in a single task, the entire batch would be held up by the slowest server in the group.

By creating individual queue items for each recipient:

  • Fast servers get messages delivered promptly
  • Slow servers don't delay delivery to others
  • Failed deliveries can be retried independently
  • Your UI remains responsive while deliveries happen in the background

It's a classic trade-off: we generate more queue messages, but gain better resilience and user experience in return.

This is particularly important in federated networks where server behavior is unpredictable and outside our control. We'd rather optimize for making sure your posts reach their destinations as quickly as possible!

What other aspects of Fedify's design would you like to hear about? Let us know!

A flowchart comparing two approaches to message queue design. The top half shows “Fedify's Current Approach” where a single sendActivity call creates separate messages for each recipient, which are individually queued and processed independently. This results in fast delivery to working recipients while slow servers only affect their own delivery. The bottom half shows an “Alternative Approach” where sendActivity creates a single message with multiple recipients, queued as one item, and processed sequentially. This results in all recipients waiting for each delivery to complete, with slow servers blocking everyone in the queue.

Coming soon in 1.5.0: Smart fan-out for efficient activity delivery!

After getting feedback about our queue design, we're excited to introduce a significant improvement for accounts with large follower counts.

As we discussed in our previous post, Fedify currently creates separate queue messages for each recipient. While this approach offers excellent reliability and individual retry capabilities, it causes performance issues when sending activities to thousands of followers.

Our solution? A new two-stage “fan-out” approach:

  1. When you call Context.sendActivity(), we'll now enqueue just one consolidated message containing your activity payload and recipient list
  2. A background worker then processes this message and re-enqueues individual delivery tasks

The benefits are substantial:

  • Context.sendActivity() returns almost instantly, even for massive follower counts
  • Memory usage is dramatically reduced by avoiding payload duplication
  • UI responsiveness improves since web requests complete quickly
  • The same reliability for individual deliveries is maintained

For developers with specific needs, we're adding a fanout option with three settings:

  • "auto" (default): Uses fanout for large recipient lists, direct delivery for small ones
  • "skip": Bypasses fanout when you need different payload per recipient
  • "force": Always uses fanout even with few recipients
// Example with custom fanout setting
await ctx.sendActivity(
  { identifier: "alice" },
  recipients,
  activity,
  { fanout: "skip" }  // Directly enqueues individual messages
);

This change represents months of performance testing and should make Fedify work beautifully even for extremely popular accounts!

For more details, check out our docs.

What other optimizations would you like to see in future Fedify releases?

Flowchart comparing Fedify's current approach versus the new fan-out approach for activity delivery.

The current approach shows:

1. sendActivity calls create separate messages for each recipient (marked as a response time bottleneck)
2. These individual messages are queued in outbox
3. Messages are processed independently
4. Three delivery outcomes: Recipient 1 (fast delivery), Recipient 2 (fast delivery), and Recipient 3 (slow server)

The fan-out approach shows:

1. sendActivity creates a single message with multiple recipients
2. This single message is queued in fan-out queue (marked as providing quick response)
3. A background worker processes the fan-out message
4. The worker re-enqueues individual messages in outbox
5. These are then processed independently
6. Three delivery outcomes: Recipient 1 (fast delivery), Recipient 2 (fast delivery), and Recipient 3 (slow server)

The diagram highlights how the fan-out approach moves the heavy processing out of the response path, providing faster API response times while maintaining the same delivery characteristics.
0

Mastodon이나 Misskey 등에서 민감한 내용으로 지정한 단문의 내용이나 첨부 이미지는 이제 Hackers' Pub에서 뿌옇게 보이게 됩니다. 마우스 커서를 가져다 대면 또렷하게 보이게 됩니다. 다만, Hackers' Pub에서 쓰는 단문을 민감한 내용으로 지정하는 기능은 없습니다. (아마도 앞으로도 없을 것 같습니다.)

Hackers' Pub 타임라인에 뜬 민감한 내용으로 지정된 단문. 내용이 뿌옇게 나와서 보이지 않는다.Hackers' Pub 타임라인에 뜬 민감한 내용으로 지정된 단문. 마우스 커서를 가져다 대면 뿌옇던 내용이 또렷하게 보인다.
0

XiNiHa shared the below article:

rel="me" 메모

Lee Dogeon @moreal@hackers.pub

서문 (동기)

아래와 같은 Hackers' Pub 글을 보았습니다:

이제 프로필의 링크에 인증 여부가 보이게 됩니다. 링크를 인증하기 위해서는, 링크된 페이지 측에서도 Hackers' Pub 프로필을 rel="me" 속성과 함께 링크해야 합니다. Mastodon이나 GitHub 같은 경우 프로필에 링크를 추가하면 rel="me" 속성이 추가되게 되어 있으니, Mastodon이나 GitHub 프로필 링크를 추가하면 인증은 자동으로 될 겁니다. 개인 웹사이트가 있으신 분들은 Hackers' Pub 프로필을 rel="me" 속성과 함께 링크하면 인증이 됩니다.

참고로 인증은 프로필 설정을 저장할 때 이뤄집니다. 이미 Mastodon이나 GitHub 프로필을 링크해 두신 분들은 인증 표시가 안 뜰 수도 있는데, 그럼 프로필 설정에 들어가셔서 저장 버튼을 한 번 눌러주시면 인증 버튼이 붙을 겁니다.

from https://hackers.pub/@hongminhee/0195ad00-50db-7bb1-b0a0-edaf9ce73515

그래서 Hackers' Pub 설정으로 들어가서 GitHub 링크를 추가하고 저장버튼을 눌러봤지만 체크 표시가 나타나지 않았습니다. rel="me"가 뭔지 잘 몰라서 https://github.com/moreal?rel=me 같이 추가해봤는데 이것도 아니라서 (아닐 것 같았지만) 찾아본 내용을 가볍게 메모로 남깁니다.

본문

rel="me"를 검색하니 MDN 문서가 반겨주었습니다. HTML <link rel="stylesheet" ... 할 때 rel 속성이었습니다. <link rel="me" 혹은 <a rel="me" 같은 느낌으로 사용할 수 있는 것 같았습니다.

https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/me

GitHub 프로필 설정에서 https://hackers.pub/@moreal 소셜 링크를 추가하면 아래처럼 링크 a 요소에 rel="me" 속성을 추가하여 줍니다.

<a rel="nofollow me" class="Link--primary wb-break-all" href="https://hackers.pub/@moreal">https://hackers.pub/@moreal</a>

이 값을 활용하여 인증 마크를 표시하는 것으로 보입니다. (Hackers' Pub 링크 인증 소스코드)

결론

GitHub 등에서 https://hackers.pub/@<id> 를 연관 링크로 추가하고 Hackers' Pub 프로필 설정에서 다시 저장 버튼을 누르면 체크 표시가 나타납니다!

여담

함께 rel 속성에 달려있는 nofollow 같은 값은 검색엔진에게 주는 힌트처럼 보입니다. 해당 링크와 연관이 있음을 보장하지 않음, 같은 의미 같습니다. 일단 지금 하려던것과 무관하니 더 찾아보지는 않았습니다.

https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel#nofollow

그리고 Hackers' Pub이 오픈소스라서 실제로 어떻게 인증마크를 표시하는지 확인할 수도 있었습니다!

Read more →
0
1
0

고등학교 졸업할 무렵부터 Vim을 썼으니까 한 20년 가까이 썼나? 중간에 Neovim으로 바꾸긴 했지만… 그런데 이제는 VS Code를 메인으로 쓴다. (물론 VSCodeVim을 쓰긴 함.) 뭔가를 끊임 없이 기름칠하고 조이고 하는 게 좀 귀찮아졌달까?

그런 의미에서 생각해 보면 셸을 바꾼다면 fish 정도가 괜찮을지도…

1

사람들이 잘 모르는 Markdown 명세 하나. CommonMark, 즉 표준 Markdown에서 URL은 자동으로 링크되지 않는다. 대부분의 Markdown 구현체들이 이를 옵션으로 지원하고, Markdown을 지원하는 웹사이트들이 이 옵션을 켜놓고 있기 때문에 모르는 사람들이 많다. CommonMark에서 URL이나 메일 주소를 링크하려면 <>로 주소를 감싸야 한다.

이를 잘 이용하면 URL의 앞 뒤에 띄어쓰기를 하지 않고도 링크를 걸 수 있다는 장점도 된다.

0

최근에 오랫동안 쓰던 키보드가 망가져서 큰 맘 먹고 프리플로우 Archon M1 PRO MAX를 질렀는데, 요즘 키보드는 다 WebHID 가지고 웹 드라이버로 설정하는 것 같다. 자바스크립트니까 뜯기 쉽겠거니 싶어서 살펴 봤는데 커스텀 HID 레포트를 보낼 수 있는 기능을 사용해서 명령들을 나열해 놓았고, 개중에는 롬을 통으로 날리는 것도 가감없이 노출되어 있길래 음 역시 WebHID 같은 건 웹에 넣을 기능이 못된다는 결론을 내렸다. 가볍게 함수 목록만 요약해서 https://gist.github.com/lifthrasiir/c79c90ecf697b1e6dc73e83f32984499에 올렸다.

2

애플리케이션 개발 측면에서 본 Drizzle ORM 대 Kysely 비교
------------------------------
# Drizzle ORM vs Kysely 비교 요약

## Drizzle ORM의 장점

- *스키마 정의의 직관성* : 선언적 방식의 스키마 정의가 가능하며, 이로부터 자동으로
CREATE TABLE SQL 생성이 가능.
- *자동화된 마이그레이션* : 스키마 변경사항을 자동으로 감지하여 SQL 마이그레이션 파일 생성이 가능.
-
직관…
------------------------------
https://news.hada.io/topic?id=19805&utm_source=googlechat&utm_medium=bot&utm_campaign=1834

0