<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Json Media</title>
    <description>Json&apos;s Playground</description>
    <link>https://json.media</link>
    <pubDate>Fri, 26 Dec 25 00:00:00 +0000</pubDate>
    <lastBuildDate>Fri, 26 Dec 25 00:00:00 +0000</lastBuildDate>
    <image>
      <title>Json Media</title>
      <url>https://json.media/images/json-logo.png</url>
      <link>https://json.media</link>
    </image>
    <managingEditor>nallwhy@gmail.com (Jinkyou Son)</managingEditor>
    <webMaster>nallwhy@gmail.com (Jinkyou Son)</webMaster>
    <item>
      <title>Using Lucide Icons in Phoenix</title>
      <description>When Heroicons aren&apos;t enough
Phoenix currently ships with Heroicons wired into the CoreComponents.icon component by default, like this.
defmodule MyApp.CoreComponents do
  @doc &quot;&quot;&quot;
  Renders a [Heroicon](https://heroicons.com).

  ...

  ## Examples

      &lt;.icon name=&quot;hero-x-mark-solid&quot; /&gt;
      &lt;.icon name=&quot;hero-arrow-path&quot; class=&quot;ml-1 w-3 h-3 animate-spin&quot; /&gt;
  &quot;&quot;&quot;
  attr :name, :string, required: true
  attr :class, :string, default: nil

  def icon(%&amp;lbrace;name: &quot;hero-&quot; &lt;&gt; _&amp;rbrace; = assigns) do
    ~H&quot;&quot;&quot;
    &lt;span class=&amp;lbrace;[@name, @class]&amp;rbrace; /&gt;
    &quot;&quot;&quot;
  end
end

Heroicons is an icon library built by Tailwind Labs, so expectations were high, but it still lacks many icons and hasn&apos;t been updated since v2.2.0 on 2024-11-18, so it looks effectively abandoned.
To make up for that, there are projects like Sidekickicons that add extra icons in the heroicons style, but it seems hard for them to stay active.
So I set things up to use Lucide in Phoenix, which has lots of icons and is continuously updated.

  
  As of v0.562.0, there are a whopping 1,666 icons.

Setting up Lucide in Phoenix
Add mix deps
Just as Heroicons is configured to fetch only the optimized folder from the tailwindlabs/heroicons GitHub repository, add Lucide to deps/0 in mix.exs so only the icons folder is fetched.
defmodule MyApp.MixProject do
  ...

  defp deps do
    [
      ...,
      &amp;lbrace;
        :lucide,
        github: &quot;lucide-icons/lucide&quot;,
        tag: &quot;0.562.0&quot;,
        sparse: &quot;icons&quot;,
        app: false,
        compile: false,
        depth: 1
      &amp;rbrace;
    ]
  end
end

Write lucide.js
Use the heroicons.js that Phoenix ships with as a reference and create lucide.js under ./assets/vendor/.
Heroicons is more complex because it creates Tailwind CSS dynamic utility classes for the four types (outline, solid, mini, micro), but Lucide is simpler. I also added a mask-size setting to match Lucide.
const plugin = require(&quot;tailwindcss/plugin&quot;)
const fs = require(&quot;fs&quot;)
const path = require(&quot;path&quot;)

module.exports = plugin(function (&amp;lbrace; matchComponents, theme &amp;rbrace;) &amp;lbrace;
  let iconsDir = path.join(__dirname, &quot;../../deps/lucide/icons&quot;)
  let values = &amp;lbrace;&amp;rbrace;

  fs.readdirSync(iconsDir).forEach(file =&gt; &amp;lbrace;
    if (file.endsWith(&quot;.svg&quot;)) &amp;lbrace;
      let name = path.basename(file, &quot;.svg&quot;)
      values[name] = &amp;lbrace; name, fullPath: path.join(iconsDir, file) &amp;rbrace;
    &amp;rbrace;
  &amp;rbrace;)

  matchComponents(&amp;lbrace;
    &quot;lucide&quot;: (&amp;lbrace; name, fullPath &amp;rbrace;) =&gt; &amp;lbrace;
      let content = fs.readFileSync(fullPath).toString().replace(/\r?\n|\r/g, &quot;&quot;)
      content = encodeURIComponent(content)
      let size = theme(&quot;spacing.6&quot;)

      return &amp;lbrace;
        [`--lucide-$&amp;lbrace;name&amp;rbrace;`]: `url(&amp;#39;data:image/svg+xml;utf8,$&amp;lbrace;content&amp;rbrace;&amp;#39;)`,
        &quot;-webkit-mask&quot;: `var(--lucide-$&amp;lbrace;name&amp;rbrace;)`,
        &quot;mask&quot;: `var(--lucide-$&amp;lbrace;name&amp;rbrace;)`,
        &quot;mask-size&quot;: &quot;contain&quot;,
        &quot;mask-repeat&quot;: &quot;no-repeat&quot;,
        &quot;background-color&quot;: &quot;currentColor&quot;,
        &quot;vertical-align&quot;: &quot;middle&quot;,
        &quot;display&quot;: &quot;inline-block&quot;,
        &quot;width&quot;: size,
        &quot;height&quot;: size
      &amp;rbrace;
    &amp;rbrace;
  &amp;rbrace;, &amp;lbrace; values &amp;rbrace;)
&amp;rbrace;)

Update app.css
To register the Lucide plugin with Tailwind CSS, add the following to app.css.
@plugin &quot;../vendor/lucide&quot;;

Add the &quot;lucide&quot; prefix to the CoreComponents.icon component
The existing CoreComponents.icon component only allows the &quot;hero&quot; prefix.
Add support for the &quot;lucide&quot; prefix like this.
defmodule MyApp.CoreComponents do
  ...

  attr :name, :string, required: true
  attr :class, :string, default: nil

  def icon(%&amp;lbrace;name: &quot;hero-&quot; &lt;&gt; _&amp;rbrace; = assigns) do
    ~H&quot;&quot;&quot;
    &lt;span class=&amp;lbrace;[@name, @class]&amp;rbrace; /&gt;
    &quot;&quot;&quot;
  end

  # Allow the lucide prefix
  def icon(%&amp;lbrace;name: &quot;lucide-&quot; &lt;&gt; _&amp;rbrace; = assigns) do
    ~H&quot;&quot;&quot;
    &lt;span class=&amp;lbrace;[@name, @class]&amp;rbrace; /&gt;
    &quot;&quot;&quot;
  end
end

Using Lucide
You can now use Lucide icons as simply as the existing Heroicons.
&lt;.icon name=&quot;lucide-newspaper&quot; /&gt;


  
  Applied directly to the header.

You can see the full code that applies Lucide to this site here.
Now you can use the massive Lucide icon set without running out. Since the icon library keeps growing quickly, update the Lucide version periodically as needed.</description>
      <link>https://json.media/blog/en/using-lucide-in-phoenix</link>
      <pubDate>Fri, 26 Dec 25 00:00:00 +0000</pubDate>
      <guid>https://json.media/blog/en/using-lucide-in-phoenix</guid>
      <category>dev</category>
    </item>
    <item>
      <title>Ash Weekly: Issue #19 Recap</title>
      <description>Ash homepage/doc revamped, AshAdmin file upload, AshAI non-OpenAI LLM, UsageRules --sync-to-folder flag, Reactor doc revamped, New Ash extensions.
Ash is currently the hottest and fast-becoming standard declarative framework in Elixir. Ash Weekly delivers weekly updates on what&apos;s happening in the Ash ecosystem, and here is a summary.
Original: Ash Weekly: Issue #19
Homepage &amp; What is Ash Guide Revamped
Since Ash is not a framework concept that typically exists in other languages, people usually need additional explanation to understand its concepts.
However, the existing explanations on the homepage and documentation were somewhat lacking. This time, the Ash homepage and the What is Ash? section of the documentation have been updated, making them much easier to understand.
If you still don&apos;t understand Ash well, be sure to check it out!
AshAdmin File Upload Support
AshAdmin is a library that automatically generates admin pages from Ash declarations.
Previously, file uploads were not possible here. This time, it has been updated to automatically support file uploads for :file type arguments. (Many of these Ash ecosystem developments are coming from community members. 👍)
The design isn&apos;t very pretty yet, but hopefully this can be improved too.
Support for Non-OpenAI Models in prompt-backed actions
The prompt-backed actions in AshAI, which makes AI-related work easier based on Ash, allow actions to be performed through LLMs.
Here&apos;s an example from the AshAI README.md for understanding:
action :analyze_sentiment, :atom do
  constraints one_of: [:positive, :negative]

  description &quot;&quot;&quot;
  Analyzes the sentiment of a given piece of text to determine if it is overall positive or negative.
  &quot;&quot;&quot;

  argument :text, :string do
    allow_nil? false
    description &quot;The text for analysis&quot;
  end

  run prompt(
    LangChain.ChatModels.ChatOpenAI.new!(%&amp;lbrace; model: &quot;gpt-4o&quot;&amp;rbrace;)
  )
end

Previously, only OpenAI models were available, but with support for various adapters that enable connections with LLMs of different characteristics, various provider LLM models are now usable.
UsageRules --sync-to-folder flag
UsageRules is a project that collects usage-rules.md files from each library, making them available as context for LLMs.
A --sync-to-folder flag has been added this time, allowing you to use a format that links to each file instead of putting all content in one file.
e.g.
&lt;-- usage-rules-start --&gt;
&lt;-- ash-start --&gt;
## ash usage
[ash usage rules](deps/ash/usage-rules.md)
&lt;-- ash-end --&gt;
&lt;-- ash_ai-start --&gt;
## ash_ai usage
[ash_ai usage rules](deps/ash_ai/usage-rules.md)
&lt;-- ash_ai-end --&gt;
&lt;-- usage-rules-end --&gt;

This way, since you&apos;re not putting all content into the context, token usage is reduced, but it might take more time to find and explore the necessary files. You can apply it according to your preferred direction.
Reactor documentation overhaul
Reactor is a library that makes it easy to use the Saga pattern. The Saga pattern is great for implementing distributed transactions.
The documentation has been revamped this time, so if you&apos;re interested in using the Saga pattern, it would be good to take a look.
Contributor Names in Changelogs
Contributors to Ash projects will have their names mentioned in release notes. This shows their commitment to building the community.
Community Extensions
AshNeo4j
A library that supports Neo4j, a representative Graph Database, for Ash.
AshOutstanding
A library that supports the Outstanding protocol for Ash.
The Outstanding protocol seems to make it easy to know whether expected values are currently satisfied, and if not, which parts are not satisfied.
AshCommanded
A library that supports Commanded, which makes it easy to apply CQRS/ES patterns to Ash.
The CQRS/ES pattern has always remained something I want to try but haven&apos;t applied yet. With the recently announced AshEvents also available, I should give it a try.
LLMs &amp; Elixir: Windfall or Deathblow
Recently, LLMs &amp; Elixir: Windfall or Deathblow written by Zach Daniel became a hot topic on Hacker News.
This content connects with the direction of recent work in Elixir like Tidewave, Phoenix.new, and UsageRules that makes AI-assisted coding easier.</description>
      <link>https://json.media/blog/en/ash-weekly-19-recap</link>
      <pubDate>Fri, 20 Jun 25 00:00:00 +0000</pubDate>
      <guid>https://json.media/blog/en/ash-weekly-19-recap</guid>
      <category>dev</category>
    </item>
    <item>
      <title>Ash Weekly: Issue #18 Recap</title>
      <description>AshPhoenix.Plug.CheckCodegenStatus, mix ash.codegen --dev, Shared Action Context, UsageRules, ash_ai.gen.chat improvements, Igniter.Scribe.
Ash is currently the hottest and fast-becoming standard declarative framework in Elixir. Ash Weekly delivers weekly updates on what&apos;s happening in the Ash ecosystem, and here is a summary.
Original: Ash Weekly: Issue #18
AshPhoenix.Plug.CheckCodegenStatus
With the AshPhoenix.Plug.CheckCodegenStatus plug, you get a friendly notification screen in Ash when code generation is required due to code changes, similar to how Phoenix notifies you about pending DB migrations.
You can also perform the required action directly by clicking the provided button.

--dev migrations
When running mix ash.codegen with the --dev option, you can accumulate codegen tasks without naming them. Later, when you run mix ash.codegen with a name, it bundles all accumulated tasks and performs codegen at once.
At DevAllCompany, we had implemented a similar feature ourselves, so it&apos;s nice to see it officially supported.
# Accumulate tasks
$ mix ash.codegen --dev

$ mix ash.codegen --dev

# Then generate all at once
$ mix ash.codegen create_user

Shared Action Context
A new shared key has been added to the context used in Ash actions. This key is passed down to nested actions called within the main action.
Since this value is propagated, avoid putting large data in it. Also, be cautious with concurrency, as values are copied between processes.
MyApp.Domain.Resource.action(..., context: %&amp;lbrace;shared: %&amp;lbrace;key: &quot;value&quot;&amp;rbrace;&amp;rbrace;)

usage-rules.md
Are you into vibe coding these days? In Elixir, rapid changes and limited training data can make LLM-generated code less satisfactory.
The UsageRules project aims to help with this.
UsageRules works simply:
It collects each library&apos;s usage-rules.md files and combines them into a single file, making it easy for LLMs to use as context.
For example, Ash&apos;s usage-rules.md explains how to understand and use Ash.
If you use Ash and Ash AI in your project, you can run the following to combine their usage rules:
$ mix usage_rules.sync CLAUDE.md ash, ash_ai

Now, when using Claude, you can provide this combined context for better vibe coding.
You can also use the --all option to gather usage rules from all libraries in use.
ash_ai.gen.chat improvements
Code generated by mix ash_ai.gen.chat now stores LLM tool calls and tool results.
Previously, if this information was missing when loading past conversations, much of the context was lost. This change should significantly improve the ability to continue conversations.
However, sometimes tool results can be very large, so it&apos;s worth considering whether saving and passing all of them to the LLM is always the best approach.
Igniter.Scribe
Igniter is a library that helps automate code changes needed for installing other libraries.
Previously, manual guides did not update automatically with code changes and had to be edited manually, but this has now been improved.
Great news for those who use Igniter while developing their own libraries.</description>
      <link>https://json.media/blog/en/ash-weekly-18-recap</link>
      <pubDate>Sun, 15 Jun 25 00:00:00 +0000</pubDate>
      <guid>https://json.media/blog/en/ash-weekly-18-recap</guid>
      <category>dev</category>
    </item>
    <item>
      <title>Ash Weekly: Issue #17 Recap</title>
      <description>Ash AI, ElixirConf EU 2025, Phoenix.new, Phoenix.Sync, AtomVM, Hologram, LangSchema.
Ash Ash is currently the hottest and rapidly becoming the standard declarative framework in Elixir. Ash Weekly is a newsletter that keeps you up to date on everything happening in the Ash ecosystem, and here’s a summary of the latest issue.
Original article: Ash Weekly: Issue #17
Ash AI Released
These days, it’s rare to find developers who aren’t considering integrating AI (LLM) into their applications. In this context, it was only natural that a powerful declarative framework like Ash would get an AI extension.
The Ash AI repo was created in August 2024 and quickly drew a lot of attention with its concept. It was officially announced at ElixirConf EU 2025. I’ve been actively using and contributing to Ash AI since March 2025 (currently I&apos;m the #2 contributor).
Ash AI offers the following features:

Prompt-backed Actions: Easily declare generic actions that return results via AI.
Tool Definition: Expose already declared actions as LLM tools with minimal effort.
Vectorization: Easily implement RAG (Retrieval-Augmented Generation) on your resources.
MCP Server: Quickly set up a development MCP server to provide app information, or a production MCP server to expose internal actions as tools.

Reference: Ash AI: A comprehensive LLM toolbox for Ash Framework
ElixirConf EU 2025 Wrap up
ElixirConf EU 2025 took place in Kraków, Poland, from May 14–16.
ElixirConf is the largest event for Elixir enthusiasts—held twice a year, with the EU edition in the first half and the US in the second half. As one of the biggest gatherings in the community, it featured a lot of interesting content, some of which is highlighted in this newsletter.
Code generators are dead, long live code generators - Chris McCord
Chris McCord and the team at fly.io introduced Phoniex.new, a remote coding agent service.
Zach Daniel described Phoenix.new as an IDE + Cloud + Elixir &amp; Phoenix-focused coding assistant.
Regardless of how you feel about the future of AI, it’s great to see these kinds of innovative attempts within the Elixir ecosystem as well. It’s currently available via waitlist, so if you’re interested, sign up!
Introducing Phoenix.Sync - James Arthur
Phoenix.Sync is an effort to tightly integrate Electric with Phoenix, making real-time sync easy to implement.
With the growing trend toward local-first applications for better user experience, Electric is a solution that’s been getting a lot of attention, so this integration is especially exciting.
Unlike LiveView, Phoenix.Sync is designed to work with React, mobile, and other front-end environments where Electric already shines, potentially broadening Phoenix’s reach.
The AtomVM and New Horizons for Elixir
AtomVM is a lightweight implementation of the BEAM, aiming to bring Erlang (and Elixir) to IoT devices and browsers.
This year’s ElixirConf EU seemed to focus more on running AtomVM in browsers, with demos like running code blocks from Hexdocs directly in-browser. I’m excited to see what new possibilities will arise from this.
Hologram: Building Rich UIs with Elixir Running in the Browser
Hologram is a project aiming to let you write both front-end and back-end in Elixir.
Whereas LiveView handles almost everything on the back-end and keeps the front-end thin, Hologram takes a different approach—allowing you to build front-ends with their own state, but all in Elixir. I haven’t fully wrapped my head around the concept yet, but I’m curious to see how far this approach can go.
Project Spotlight - LangSchema
Currently, Ash AI supports only OpenAI models. This is because each AI provider’s JSON schema spec is different. To solve this, I’ve developed a library called LangSchema.
By introducing the concept of an abstract schema, you can write your code once and have it converted to the JSON schema required by each AI provider.
If you’re working with AI, you might want to give it a try.</description>
      <link>https://json.media/blog/en/ash-weekly-17-recap</link>
      <pubDate>Mon, 26 May 25 00:00:00 +0000</pubDate>
      <guid>https://json.media/blog/en/ash-weekly-17-recap</guid>
      <category>dev</category>
    </item>
    <item>
      <title>오너십(주인의식)에 대하여</title>
      <description>주인이 아닌데 왜 주인의식을 가지라 그래??

그때는 전세나 월세로 살고 있다면 집을 꾸밀 수 있다는 생각은 전혀 하지 못했고 심지어 고장 나도 고치지 않고 살았던 모습이 떠오릅니다. 거주하는 집의 주인이 따로 있기 때문에 세입자들은 잠시 거쳐가는 집이라고 인식하는 경향이 강했기 때문인 것 같아요. 그런 분위기 속에 월세집을 자기 돈 써서 고치는 세입자가 있다는 사실에 첫째로 놀랐습니다.
-  뉴스레터 &apos;좋아하는 장소가 집이 아닌가요?&apos; 중

몇 달 전에 점핏 개취콘에서 &apos;중요한 건 인터페이스야, 바보야&apos; 라는 주제로 강연을 할 기회가 있었다.

  
  좋은 기회 감사합니다.

(강연 다시보기: 2023 두번째 개취콘, 백엔드 개발자 이야기 JUMPIT TO BACK - END)
강연이 끝나고 질문을 받는 시간이 있었는데, 아래와 같은 질문을 받았다.
&quot;함께 일하는 주니어 개발자 분들 중에 &apos;아 이 친구 정말 센스있게 일한다&apos; 라고 생각했던 분이 분명 있으실 것 같은데 어떤 부분에서 그렇게 느끼셨을지 궁금합니다.&quot;
그래서 &apos;오너십이 있는 사람과 일하는 경험이 좋았다&apos; 라는 내용으로 대답을 했는데, 어떤 분이 유튜브 채팅창에 이런 말을 올리셨다.
&quot;
신입한테 무슨오너까지..
직장인이 오너처럼 태도갖는사람 살면서본적이없음
오너만큼 돈받으면몰라도
&quot;
저 말대로 오너가 아닌 사람에게 오너십을 가져야한다고 얘기할 수는 없다.

내 집에 아닌 곳을 빌려서 살고 있는데, 집이 내 맘에 썩 들지 않으면 어떻게 할 것인가.
나는 내가 집의 주인이 아니라는 이유로 매일 집에 들어올 때 안 좋은 기분을 느끼고, 사람들을 초대할 때마다 집이 마음에 들지 않는 점들을 얘기하며 살아야 하는 사람은 누구인가?
내가 오너십을 가지고 집을 관리하면 집 주인이 덕을 보겠지. 하지만 그게 싫어서 오너십을 가지지 않으면 집이 좋아하는 공간이 될 수 없다.
내가 집의 주인이 아니라는 이유로 내 삶의 주인도 남에게 맡기는 것은 아닐까.

항상 오너십을 가져야한다는 것은 아니다. 그러기 좋지 않은 상황도, 그러기 어려운 상황도 있다.
다만 내가 오너십을 가지기로 선택했는지 가지지 않기로 선택했는지 명시적으로 생각해볼 필요는 있다.</description>
      <link>https://json.media/blog/ko/about-ownership</link>
      <pubDate>Sun, 05 Nov 23 00:00:00 +0000</pubDate>
      <guid>https://json.media/blog/ko/about-ownership</guid>
      <category>essay</category>
    </item>
    <item>
      <title>&apos;포기사회&apos;를 지나 &apos;파괴사회&apos;로 가고 있는 한국</title>
      <description>희망이 없으면 파괴하지 않을 이유가 있을까?
지난 글 포기하지 않기 위해서는 핑계가 필요해 에서 한국이 이미 &apos;피로사회&apos;에서 &apos;포기사회&apos;가 되었다고 얘기했다.

  

포기사회의 다음은 어디일까? 나는 &apos;파괴사회&apos; 라고 생각한다.
룰은 희망이 있을 때 지킬 이유가 있다. 룰을 어기면 사회의 처벌로 인해 미래에 대한 희망이 깨지기 때문이다.
반대로 희망이 없으면 룰을 어겨도 딱히 아쉬울 것이 별로 없다. 오히려 얻을 것이 더 많을 수도 있다. 분노 해소, 쾌감, 세상의 관심...
희망을 모두 포기하고 나면 파괴를 하지 않을 이유가 무엇이 남을까?
우리나라도 요즘 묻지마 살인 등이 파괴 범죄가 발생하고 있는데, 전체적인 범죄율 자체는 낮지만 포기한 혹은 포기당한 사람들이 이러한 파괴로 이어지고 있다고 생각한다.

  
  “남들도 불행하게 만들고 싶었다” https://biz.heraldcorp.com/view.php?ud=20230724000215

우리나라에서 포기를 하는 사람의 수는 계속 늘고 있다. 2022년 우리나라의 은둔형 외톨이 청년의 수는 24만명 규모로 추산되며, 이는 청년 인구의 2.4% 에 해당한다. 히키코모리로 유명한 일본보다도 한참 높은 수치라고 한다.

  
  https://www.seoul.co.kr/news/newsView.php?id=20230307500184

우리나라보다 일찍이 포기사회에 들어간 일본은 사회적 고립을 해소하기 위한 움직임이 일어나고 있다.

  
  https://www.khan.co.kr/world/world-general/article/202308061426001

사회 전반에 어떤 큰 움직임이 발생한다면 이는 사회의 압력이 발생시키는 문제다. 하지만 아직 우리나라에서는 이를 사회의 문제로 보기보다 개인의 문제로 보는 시각이 강하다.
예를 들어 학교에서 문제가 되는 학생은 퇴학은 시키면 되고, 범죄를 저지르는 사람은 감옥에 보내면 되며, 정신병이 생긴 사람은 정신병원에 보내면 된다는 것이다. 문제가 되는 사람을 사회에서 도태시키면 문제가 해결된다고 생각한다.
하지만 눈앞에서 보이지 않는다고 없어지는 것은 아니다. 이렇게 도태된 개인이 사라지면 해결되는 문제라는 피상적인 인식 때문에 본질적인 해결책은 논의되기 어려울 것이며, 앞으로도 사회 파괴적 범죄는 더 많아질 것으로 예상한다.
그리고 문제가 한참 더 심각해지고 나서야 이를 제대로 들여다보기 시작할 것이다.</description>
      <link>https://json.media/blog/ko/from-resignation-society-to-destruction-society</link>
      <pubDate>Wed, 25 Oct 23 00:00:00 +0000</pubDate>
      <guid>https://json.media/blog/ko/from-resignation-society-to-destruction-society</guid>
      <category>essay</category>
    </item>
    <item>
      <title>포기하지 않기 위해서는 핑계가 필요해</title>
      <description>핑곗거리가 부족한 포기사회의 사람들을 위하여

마모루: 네가 변명하길 바랐다.
일보: 그, 그치만 KO패 인데요? 완패인데? 변명 같은 걸 하면... 화내실 텐데요.
마모루: 화내는 게 당연하지. 등짝을 철썩철썩 때릴지도 몰라.
기뻐서 말이야.
보기 흉하고, 비루하고, 뻔뻔해도 돼. 아무리 꼴사나워도 괜찮아.
넌 아직 지지 않았다. 한 번 더 붙으면 이길 수 있다.
이런 말을 하란 말이야.
인정할 때가 끝나는 때다. 한 번 더 일어설 거면, 남자한테는 변명이 필요해.
-  109권 중

개인적으로 자기합리화를 잘해서 늘 핑계가 있는 편이다.
고등학교 때 성적이 나보다 잘 나오는 친구가 있을 때 &quot;그런데 쟤는 공부만 잘하잖아. 나는 여러 가지를 하니까.&quot;, 준비했던 결과가 잘 나오지 않았을 때 &quot;이번 거는 최선을 다 하지 않았어. 다른 거를 더 열심히 하려고 했어&quot;.
그런 성격 때문에 그동안 여러 사건들에서 좌절하지 않고 계속 해올 수 있었나 싶기도 하다.

한병철은  에서 저자는 과잉 긍정성의 성과사회에서 피로해지는 개인에 대한 이야기를 담았다.

&apos;아무것도 가능하지 않다&apos;는 우울한 개인의 한탄은 &apos;아무것도 불가능하지 않다&apos;고 믿는 사회에서만 가능한 것이다. 더 이상 할 수 있을 수 없다는 의식은 파괴적 자책과 자학으로 이어진다.
-  중

현대 사회는 개인에게서 불가능에대한 핑곗거리를 앗아갔다.
&quot;공부를 하고 싶어? 너는 하버드의 수업을 무료로 들을 수 있어. 옛날에는 책을 못사서 줏어다가 공부를 했어. 지금 시대에 공부를 못한다면 오로지 원인은 너야.&quot;
그래서 뭔가를 실패한 개인에게는 한 번 더 시도할 수 있는 원동력인 핑계가 없다. 그저 포기하게 될 뿐이다.
이 책으로 2016년 트레바리에서 독서모임을 했을 때, &apos;피로사회&apos;의 다음은 무엇일까에 대해 얘기 나눴었고 나는 &apos;포기사회&apos;일 것이라고 말했었다.
일본은 그때 당시 프리터, 히키코모리로 대표되는 사회 문제를 겪고 있었고, 우리나라도 이미 피로사회의 단계에 들어선 상태에서 일본을 따라가고 있다고 생각했기 때문이다.
그리고 2023년인 지금 이미 &apos;포기사회&apos;에 도달했다고 생각한다. 대표적으로 결혼과 출산을 포기해서 매우 낮은 혼인율과 출산율을 보이고 있고, 미래에 대한 희망을 포기해서 현재를 위한 과한 소비를 하고 있고, 보이지 않는 곳에서는 고독사가 일어나고 있다.
(간혹 &apos;피로사회&apos;의 해석 중에 탈진의 피로가 아닌 무위의 피로를 기반으로 &apos;현대사회 = 성과사회  피로사회&apos;의 개념으로 이야기 하려는 해석들이 있으나, 해당 내용은 책의 말미에 나오는 무위의 피로에 대한 이야기의 연장일 뿐 저자의 다른 원고 ,  등으로 미루어보았을 때 저자는 &apos;현대사회 = 성과사회 = 피로사회&apos; 라고 말하고자 하는 것이라고 생각한다.)

오히려 더 증가하고 있는 것은 재도전이 아닌 포기를 위한 핑계다. 포기를 위한 핑계는 현상이 아니라 자기 본질에 대해 핑계를 댄다.
예를 들면 성인 ADHD 가 그렇다. 나도 내가 성인 ADHD 이기를, 그래서 어쩔 수 없는 한계 짓기의 핑계를 얻을 수 있기를 기대했었으나, 검사 결과 그런 건 아니라고 한다.
MBTI 도 어찌보면 자신을 한계 짓고 포기하기 위한 핑계로 유행하고 있는지도 모른다.
매크로하게 사라져버린 원동력의 핑계를 개인이 찾아오기는 힘들다.

라고 쓰고보니, 내가 재도전을 위한 핑계를 말하는 것이 과잉 긍정성인가 싶다. 포기하면 편할 수도 있지 않은가?
무결론의 마무리</description>
      <link>https://json.media/blog/ko/excuse-to-avoid-giving-up</link>
      <pubDate>Sat, 23 Sep 23 00:00:00 +0000</pubDate>
      <guid>https://json.media/blog/ko/excuse-to-avoid-giving-up</guid>
      <category>essay</category>
    </item>
    <item>
      <title>당연한 것처럼 말하는 잘못된 가정 속에 역린이 있다</title>
      <description>건드리고 싶지만 건드리지 않는 편이 나은
어떤 사람과 논쟁을 할 때 그 사람이 충분히 논리적인 사람임에도 전혀 논리적이지 않은 논리의 근원이 있다면, 그것은 건드리지 않는 것이 좋다.
그곳에는 그 사람이 그렇게 하고 싶지 않았지만 그렇게 해버리고만 부끄러움이 있다.
인간이 그런 부끄러움에 대해 대응하는 방법은 크게 두 가지이다.

처음부터 솔직하게 말한다.
거기까지 상대방이 파고들지 않기를 바라면서 거짓말을 한다.

그런 것을 처음부터 솔직하게 말할 수 있는 사람이 얼마나 있을까?
대부분 제발 여기까지 파고들지 않기를 기대하는 마음으로 거짓말을 하며 버틴다.
그러다 그것이 파내어지고나면 그 사람에게 남은 선택지가 별로 없게 된다.

이제와서 자신도 그것이 아님을 알면서도 거짓말을 했음을 솔직히 인정한다.
상대방에게 화를 낸다.

여기서도 마찬가지로, 이제와서 앞의 모습은 다 알면서도 한 거짓이었음을 솔직하게 인정할 수 있는 사람이 얼마나 있을까?
대부분은 후자의 결과를 보게 될 것이다.
그것이 그렇게 궁금한가?

Cover Image: generated by DALL·E 2 with prompt &apos;There&apos;s a hidden danger in the incorrect assumptions spoken as if they&apos;re obvious it&apos;s better not to touch it even if one wants to&apos;</description>
      <link>https://json.media/blog/ko/hidden-danger-in-illogical-assumptions</link>
      <pubDate>Wed, 20 Sep 23 00:00:00 +0000</pubDate>
      <guid>https://json.media/blog/ko/hidden-danger-in-illogical-assumptions</guid>
      <category>essay</category>
    </item>
    <item>
      <title>하지 않아도 되는데 굳이 하는 행동에 욕망이 있다</title>
      <description>부러우니까 자랑을 하고 자랑을 하니까 부러워지고

부러우니까 자랑을 하고 자랑을 하니까 부러워지고
-  - 장기하

예를 들어 누군가 누가 묻지도 않았는데 굳이 &quot;나는 이러이러한 사람이야&quot; 라고 말한다면, 그것은 다른 사람들이 나를 그런 사람으로 알아줬으면 하는 욕망 때문일 것이다.
너무 뻔한 얘기인가?
그 사람이 정말 내가 &quot;그러그러한&quot; 사람인 것 자체가 중요했다면 굳이 저렇게 얘기하지 않았을 것이다.
내가 그런 사람인데 사람들이 그렇다고 생각해주지 않는 것은 실제로는 그런 사람이 아니기 때문일 가능성이 높다.
그렇다면 그 사람은 자신이 되고 싶은 자신과 실제 자신 사이에 괴리가 있는 것이다.
그것이 사람의 욕망이다.
그 괴리로 인한 욕망은 좀처럼 채워지지 않는다. 왜냐하면 실제로 자신이 원하는 사람이 될 수 있는 경우는 잘 없기 때문이다.
그 욕망을 잘 파악해서 건드린다면, 그 사람을 내가 원하는 대로 다루기 쉬워진다.

나는 이 글을 왜 썼을까?

Cover Image: generated by DALL·E 2 with prompt &apos;person who is jealous, photo, no text&apos;</description>
      <link>https://json.media/blog/ko/desire-in-not-necessary-actions</link>
      <pubDate>Mon, 11 Sep 23 00:00:00 +0000</pubDate>
      <guid>https://json.media/blog/ko/desire-in-not-necessary-actions</guid>
      <category>essay</category>
    </item>
    <item>
      <title>B2B SaaS 의 보안을 높여주는 데이터 암호화 in Elixir</title>
      <description>Elixir 로 B2B SaaS 를 만드는 여정 - (2)
Elixir 로 B2B SaaS reflow 를 만들면서 경험한 내용 시리즈입니다.

B2B SaaS 의 보안을 높여주는 Multi-Tenancy in Elixir
B2B SaaS 의 보안을 높여주는 데이터 암호화 in Elixir
B2B SaaS 의 보안을 높여주는 Redaction in Elixir
Job Scheduling 예정
Feature Flag 예정
Obfuscation 예정


B2B SaaS 는 대체로 고객의 민감한 정보를 다루기 때문에 암호화의 필요성을 더 말할 필요는 없을 것 같습니다.
이번 글에서는 암호화가 무엇이고, Elixir 에서 암호화를 어떻게 구현할 수 있는지 알아봅니다.
암호화란 무엇인가
암호화는 어떤 정보를 encoding 하여, 나중에 권한을 가지고 있는 주체만 이를 복호화해서 정보를 볼 수 있도록 하는 것을 말합니다.

In cryptography, encryption is the process of encoding information. This process converts the original representation of the information, known as plaintext, into an alternative form known as ciphertext. Ideally, only authorized parties can decipher a ciphertext back to plaintext and access the original information.
- Wikipedia - Encryption

저장되거나 전송되는 고객의 정보가 절대 탈취당하지 않으면 좋겠지만, 불의의 사고는 항상 일어나기 마련입니다. 암호화는 누군가 저장되거나 전송되는 정보를 탈취하더라도, 그 정보를 해석할 수 없게 하여 최종적으로 고객 정보의 유출을 막기위해 사용됩니다.
예를 들어 데이터베이스에 저장되어있던 데이터들이 탈취당한다고 하더라도, 데이터가 암호화 되어있다면 암호화 키도 같이 탈취당한 것이 아닌 이상 데이터 유출 걱정은 적습니다.
간단한 암호화 방법의 예
간단한 암호화 방법을 한 번 살펴볼까요?
Caesar cipher 는 고대 로마의 황제 시저가 사용했던 암호화 방법으로, 암호화 하고자 하는 글의 알파벳을 일정 차이만큼 다른 알파벳으로 바꾸는 암호화 방법입니다.
예를 들어, A -&gt; C, B -&gt; D, C -&gt; E 이렇게 2글자 차이만큼 다른 알파벳으로 바꿔서 적는 것이죠.
그러면 APPLE -&gt; CRRNG 와 같이 암호화되어 원래 내용을 쉽게 알아볼 수 없게 됩니다.

  
  Caesar cipher 는 이런 기계를 이용하여 쉽게 암호화/복호화 할 수 있습니다.

이러한 암호화를 공격하는 방법에는 대표적인 몇 가지 방법들이 있습니다.
암호화 키 탈취
암호화는 복호화를 할 수 있어야 하기에, 암호화된 정보를 복호화 할 수 있는 키가 존재합니다. 따라서 이 키를 탈취하면 쉽게 암호화된 정보를 복호화 할 수 있습니다. 위의 예시에서 CRRNG 가 2글자 차이로 암호화 되었다는 사실을 알면 APPLE 로의 복호화는 매우 쉽습니다.
통계적인 특성으로 유추
Caesar cipher 로 긴 영어 문장을 암호화했다고 가정합시다. 우리는 영어에서 e 가 가장 빈도가 높은 알파벳임을 알고 있습니다. 따라서 암호화된 정보에서 빈도가 높은 알파벳들이 e 라는 가정하에 복호화를 시도하면 이를 쉽게 성공할 수 있습니다.
반복적인 내용으로 유추
Caesar cipher 로 두 사람간의 편지를 암호화했다고 가정합시다. 오가는 편지를 여러 개 모아서 보면, 편지가 시작할 때 자주 보이는 패턴이 Hi 같이 자주 사용되는 인사가 아닐까 생각해볼 수 있습니다. 이런 방식으로 암호화된 정보를 많이 모아서 규칙을 찾아 암호화를 공격할 수 있습니다.
무차별 대입 (Brute Force)
Caesar cipher 는 암호화 할 수 있는 방법이 알파벳 개수인 26가지 밖에 되지 않습니다. 따라서 26가지 방법을 다 시도하면 복호화를 할 수 있습니다.
이 밖에도 많은 암호화 공격 방법이 있습니다.
현대 암호화 방법은 키의 값이 매우 크다든지, 같은 정보를 반복적으로 암호화 해도 다른 결과로 암호화 된다든지, 주기적으로 키를 rotation 할 수 있다든지 등의 방법으로 여러가지 공격에 최대한 대응할 수 있도록 설계되어있습니다.
Elixir 로 데이터 암호화 구현하기
Cloak 은 Elixir 에서 데이터를 암호화를 도와주는 라이브러리로, 아래와 같은 특징을 가지고 있습니다.

AES-GCM, AES-CTR 두 가지 암호화 방법을 자체적으로 지원하며, 원하면 직접 암호화 방법을 구현하여 사용할 수도 있습니다.
Random IV(Initialization Vector) 기능을 제공하기 때문에 같은 정보를 반복적으로 암호화 해도 매번 다른 결과로 암호화 되어 보안적으로 실수하지 않게 해줍니다.
암호화 결과에 태그 정보가 있어서, 나중에 암호화 키를 rotation 하기 용이합니다.
Cloak.Ecto 도 함께 제공하여, 데이터베이스 암호화에 쉽게 사용할 수 있습니다. (Ecto 는 Elixir 에서 데이터베이스 관련 작업을 도와주는 라이브러리입니다.)

Cloak 으로 데이터베이스에 저장하기/불러오기 시 자동으로 암호화/복호화 되도록 하는 작업해보겠습니다.
Cloak 설정하기
먼저 Cloak, Cloak.Ecto 를 dependency 에 추가해줍니다.
defmodule MyApp.MixProject do
  use Mix.Project

  ...

  defp deps do
    [
      ...
      &amp;lbrace;:cloak, &quot;~&gt; 1.1&quot;&amp;rbrace;,
      &amp;lbrace;:cloak_ecto, &quot;~&gt; 1.2&quot;&amp;rbrace;
    ]
  end
end

다음으로 암호화/복호화를 담당하는 Vault module 을 만들고, config 를 추가해줍니다. 여기서는 암호화 방법으로 AES-GCM 을 이용했습니다.
defmodule MyApp.Vault do
  use Cloak.Vault, otp_app: :my_app
end

import Config

...

config :my_app, MyApp.Valut,
  ciphers: [
    default: &amp;lbrace;Cloak.Ciphers.AES.GCM, tag: &quot;AES.GCM.V1&quot;, key: Base.decode64!(&quot;your-key-here&quot;)&amp;rbrace;
  ]

cipher key 는 IEx(Interactive Shell) 등에서 아래와 같은 방법으로 쉽게 만들 수 있습니다.
iex&gt; 32 |&gt; :crypto.strong_rand_bytes() |&gt; Base.encode64()
&quot;HXCdm5z61eNgUpnXObJRv94k3JnKSrnfwppyb60nz6w=&quot;

마지막으로 Application supervison tree 에 추가해주면 Cloak 을 사용할 준비는 끝납니다.
defmodule MyApp.Application do
  use Application

  ...

  def start(_type, _args) do
    children = [
      ...,
      MyApp.Vault
    ]
  end
end

Cloak 으로 암호화/복호화 하기
위에서 만든 MyApp.Vault module 로 아래와 같이 쉽게 암호화/복호화를 할 수 있습니다.
iex&gt; &amp;lbrace;:ok, ciphertext&amp;rbrace; = MyApp.Vault.encrypt(&quot;plaintext&quot;)
&amp;lbrace;:ok, &lt;&lt;1, 10, 65, 69, 83, 46, 71, 67, 77, 46, 86, 49, 93, 140, 255, 234,
1, 195, 125, 112, 121, 186, 169, 185, 129, 122, 237, 161, 160, 24, 166,
48, 224, 230, 53, 194, 251, 175, 215, 10, 186, 130, 61, 230, 176, 102,
213, 209, ...&gt;&gt;&amp;rbrace;

iex&gt; MyApp.Vault.decrypt(ciphertext)
&amp;lbrace;:ok, &quot;plaintext&quot;&amp;rbrace;

Cloak.Ecto 로 데이터베이스 저장하기/불러오기 시에 자동으로 암호화/복호화 하기
먼저 각 Vault, 각 type 별로 local Ecto type 을 선언해줘야 합니다. 여기서는 MyApp.Vault 로 Binary(String) 을 저장하기 위한 local Ecto type 을 선언해보겠습니다.
defmodule MyApp.Encrypted.Binary do
  use Cloak.Ecto.Binary, vault: MyApp.Vault
end

다음으로 새로 User 를 만들면서 email 을 암호화 하고자 하는 시나리오로 구현해보겠습니다.
users table 을 만들 때 encrypted_email column 을 binary type 으로 설정해줍니다. (email 로 만들어도 되지만 column 명이 명확한 것을 좋아합니다.)
defmodule MyApp.Repo.Migrations.CreateUsers do
  use Ecto.Migration

  def change do
    create table(:users) do
      field :encrypted_email, :binary, null: false
    end
  end
end

defmodule MyApp.Accounts.User do
  use Ecto.Schema

  schema &quot;users&quot; do
    field :email, MyApp.Encrypted.Binary, source: :encrypted_email
  end
end

이렇게 field type 만 설정해주면 데이터를 데이터베이스에 저장하고 불러올 때 자동으로 암호화/복호화가 되어 어플리케이션에서는 신경쓸 것이 없습니다.
MyApp.Repo.get(MyApp.Accounts.User, 1)
# =&gt; %Accounts.User&amp;lbrace;email: &quot;test@example.com&quot;&amp;rbrace;


이렇게 Elixir 에서는 적은 코드의 수정으로 전체 어플리케이션에 데이터 암호화를 적용할 수 있습니다.
이는 더 빠르고 안전한 B2B SaaS 개발로 이어집니다.
다음 글에서는 &apos;B2B SaaS 의 보안을 높여주는 Redaction in Elixir&apos; 에 대해 얘기해보도록 하겠습니다.
감사합니다!

References:

Why Your SaaS App Needs Better Encryption
</description>
      <link>https://json.media/blog/ko/data-encryption-in-elixir</link>
      <pubDate>Thu, 17 Aug 23 00:00:00 +0000</pubDate>
      <guid>https://json.media/blog/ko/data-encryption-in-elixir</guid>
      <category>dev</category>
    </item>
  </channel>
</rss>