Jaeyeol Lee

@kodingwarrior@hackers.pub · 265 following · 185 followers

Neovim Super villain. 풀스택 엔지니어 내지는 프로덕트 엔지니어라고 스스로를 소개하지만 사실상 잡부를 담당하는 사람. CLI 도구를 만드는 것에 관심이 많습니다.

Hackers' Pub에서는 자발적으로 바이럴을 담당하고 있는 사람. Hackers' Pub의 무궁무진한 발전 가능성을 믿습니다.

그 외에도 개발자 커뮤니티 생태계에 다양한 시도들을 합니다. 지금은 https://vim.kr / https://fedidev.kr 디스코드 운영 중

Github
@malkoG
Blog
kodingwarrior.github.io
mastodon
@kodingwarrior@silicon.moe

Node.js 개발자라면 꼭 읽어봤으면 하는 아티클 2선

일반적인 Node.js 애플리케이션을 개발할 때 프레임워크와 무관하게 함께 일하는 동료들에게 필수적으로 추천하고 싶은 아티클이 있다.

빠르고 유지보수 가능한 데이터베이스 패턴들

https://sophiebits.com/2020/01/01/fast-maintainable-db-patterns

특정 프레임워크나 ORM에 의존하지 않고도 N+1 Query, 캐싱 같은 일반적인 문제를 해결하며 유지보수 가능한 형태로 코딩하는 방법을 제시한다. DataLoader로 모든 문제가 해결된다고 생각할 수도 있지만, 근본적인 문제 해결 과정을 직접 고민해보는 것만으로도 엔지니어링 역량 향상에 큰 도움이 된다.

Next.js에서 보안을 고려하는 방법

https://nextjs.org/blog/security-nextjs-server-components-actions

React 관련 내용이 포함되어 있지만, 그 부분을 제외하고 읽어도 충분히 가치가 있다. 데이터를 클라이언트에 전달할 때 마스킹하거나 접근 권한을 검사하는 방법을 구체적으로 알려준다. 핵심은 별도의 플러그인이나 외부 시스템 없이도 간단하면서 효과적인 권한 검사 시스템을 구축할 수 있다는 점이다.

마무리

위 아티클들을 추천하는 이유는 간단하다. 특정 프레임워크에 결속되지 않으면서도 독립적이고 지역적으로 백엔드 애플리케이션에서 볼 수 있는 일반적인 문제를 해결할 수 있는 방법들을 제시하기 때문이다. 이런 접근법은 전체 코드베이스의 안전성과 성능을 크게 향상시키며, 결국 더 많은 개발자들이 행복하게 일할 수 있는 환경을 만들어준다고 생각한다.

대부분 프레임워크에 의존적이거나 플러그인에 의존해 전체 코드베이스를 올바르게 수정하기 어려워지는 모습을 여러번 보았다. 개인적으로 엔지니어링 문화에서 크게 해결하고 싶은 부분이다. JavaScript 뿐만 아니라 가능하면 언어나 런타임에 있는 근원적 요소만을 통해 문제를 해결하는게 건강하게 문제를 해결할 수 있는 방법이라고 본다.

6

vim을 쓸 사람은 다른 에디터를 써도 vim 모드를 쓰거나 만들거나 해서 어떻게든 vim처럼 쓰려고 하지만 결국 부족함을 느끼고 vim이나 neovim으로 돌아간다는 그런

3

한국 페디버스 개발자 모임에서 주최한 스프린트에 다녀왔다. 기여하고자 했던 github.com/fedify-dev/fedify/i 는 또 JSON-ish 관련 이슈로 해결하지 못했지만 소규모 모임에서 얻을 수 있는 좋은 분위기와 에너지를 맘껏 느껴 좋았다. PR은 보내지 못했지만 Fedify 마스코트(이름 없음)로 키링 만들어 나눠드려 그나마 다행이었다. 행사 준비하고 진행해준 @hongminhee洪 民憙 (Hong Minhee) 님께 감사드린다.

페디버스 행사가 있는 날에 하필 트위터 서비스가 고장나서 오랜만에 마스토돈에 글 남겨본다 ㅋㅋ

페디파이 마스코트로 만든 키링맥북도 오랜만 코딩도 오랜만 스프린트도 오랜만지하철 2호선 타고 성수로 넘어가며 본 풍경이 상쾌했다.
5

다른 페디버스 서버에서 온 메시지라 그런가, 약간 외계인이 보낸 전파 신호를 감지한 느낌이 든다.

5

AI직원의 출근거부사태에 이어, 업데이트가 몇분째 없길래 뭐하고 있냐고 물으니까 황급하게 다시 일하기 시작했다(아마 이용자가 몰려서인것으로 예상). 졸기까지하고 사람 다 됐구만.

6

I've been thinking about adding a debug dashboard to that shows all activities being sent and received in real-time. This would include filters by activity type, detailed inspection of JSON-LD content, signature verification details, and retry management for failed deliveries.

As a , would you find this useful for troubleshooting federation issues? Any other features that would be helpful in such a debugging tool?

1

The Official Python Documentary has published their teaser trailer at PyCon US 2025, and I'm so honored to be part of the story of Python along with other prominent Python figures.

Check the trailer, and watch the official launch of the documentary at EuroPython later this year.

youtu.be/pqBqdNIPrbo

0

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

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

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

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

7
1
3

Jaeyeol Lee shared the below article:

논리와 메모리 - 논리와 저수준(Low-level) 자료 표현(Data representation) (2 편 중 2 편)

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

이 글은 "논리적"이 되는 두 번째 방법인 논건 대수를 재조명하며, 특히 컴퓨터 공학적 해석에 초점을 맞춥니다. 기존 논건 대수의 한계를 극복하기 위해, 컷 규칙을 적극 활용하는 반(半)공리적 논건 대수(SAX)를 소개합니다. SAX는 추론 규칙의 절반을 공리로 대체하여, 메모리 주소와 접근자를 활용한 저수준 자료 표현과의 커리-하워드 대응을 가능하게 합니다. 글에서는 랜드(∧)와 로어(∨)를 "양의 방법", 임플리케이션(→)을 "음의 방법"으로 구분하고, 각 논리 연산에 대한 메모리 구조와 연산 방식을 상세히 설명합니다. 특히, init 규칙은 메모리 복사, cut 규칙은 메모리 할당과 초기화에 대응됨을 보여줍니다. 이러한 SAX의 컴퓨터 공학적 해석은 함수형 언어의 저수준 컴파일에 응용될 수 있으며, 논리와 컴퓨터 공학의 연결고리를 더욱 강화합니다. 프랭크 페닝 교수의 연구를 바탕으로 한 SAX는 현재도 활발히 연구 중인 체계로, ML 계열 언어 컴파일러 개발에도 기여할 수 있을 것으로 기대됩니다.

Read more →
12

Next.js 서버 액션은 서버 데이터를 가져오는 용도로 사용하기에 적합하지 않다. React 공식문서에서는 다음과 같이 말하고 있다.

Server Functions are designed for mutations that update server-side state; they are not recommended for data fetching. Accordingly, frameworks implementing Server Functions typically process one action at a time and do not have a way to cache the return value.

서버 액션이 여러 호출되면 바로 실행되는 대신 큐에 쌓이고 순차적으로 처리된다. 이미 실행된 서버 액션은 이후 취소할 수 없다.

이에 서버 액션을 데이터 가져오기로 활용하면 끔찍해지는 UX가 생길 수 있는데, 예를 들어 페이지의 목록 검색 화면에서 검색 후 데이터를 가져오는 상황에 않았다고 다른 화면으로 네비게이션이 불가능한 것은 일반적인 경험이 아니다.

이러면 RSC를 통해 무한 스크롤을 구현하지 못하는가? 에 대해서 의문이 생길 수 있는데 여기에 대해서 대안을 발견했다.

function ServerComponent({ searchParams }) {
  const page = parseInt((await searchParams).page ?? "1", 10)
  const items = await getItems(page)
  return (
    <Collection id={page}>
      {items.map(item => <InfiniteListItem key={item.id} {...items} />)}
    </Collection>
  )
}
"use client"

function Collection({ id, children }) {
  const [collection, setCollection] = useState(new Map([[id, children]]))
  const [lastId, setLastId] = useState(id)
  if (id !== lastId) {
    setCollection(oldCollection => {
      const newCollection = new Map(oldCollection)
      newCollection.set(id, children)
      return newCollection
    })
    setLastId(id)
  }
  return Array
    .from(collection.entries())
    .map(
      ([id, children]) => <Fragment key={id}>{children}</Fragment>
    )
}

대충 이런 꼴이다. 이러고 page를 증가시키거나 감소시키는건 intesection observer나 특정 엘리먼트의 onClick 이벤트 따위를 의존하면 된다. 이러면 데이터 가져오기 패턴을 RSC 형태로 의존할 수 있다. InfiniteListItem는 서버컴포넌트, 클라이언트컴포넌트 무엇으로 구현하더라도 상관없다. 가령 아래와 같은 식:

function ServerComponent({ searchParams }) {
  const page = parseInt((await searchParams).page ?? "1", 10)
  const { items, hasNext } = await getItems(page)
  return (
    <div>
      <Collection id={page}>
        {items.map(item => <InfiniteListItem key={item.id} {...items} />)}
      </Collection>
      {hasNext && (
        <IntersectionObserver nextUrl={`/?page=${page + 1}`} />
      )}
    </div>
  )
}

검색 조건이나 검색어에 따라 상태를 초기화시키려면 다음과 같이 표현하면 된다.

function ServerComponent({ searchParams }) {
  const page = parseInt((await searchParams).page ?? "1", 10)
  const query = parseInt((await searchParams).query ?? "")
  const { items, hasNext } = await getItems(page, query)
  return (
    <div>
      <Form action="/">
        <input name="query" />
        <button />
      </Form>
      <Collection id={page} key={query}>
        {items.map(item => <InfiniteListItem key={item.id} {...items} />)}
      </Collection>
      {hasNext && (
        <IntersectionObserver nextUrl={`/?page=${page + 1}&query=${query}`} />
      )}
    </div>
  )
}

매우 PHP스럽고, 암묵적이기도 하다. 다만 RSC의 데이터 가져오기 패턴을 활용하면서 기존 컴포넌트를 최대한 재사용할 수 있게 된다는 점이 좋다.

2

커서를 제대로 사용하는 12가지 방법
------------------------------
* 커서 디자이너가 말하는 커서 사용법인데, 잘 몰랐던 내용들이 있어 공유드립니다.
1. Cursor는 프로젝트 별로 규칙(Rules)을 세울 수 있음. Cursor > Setting > Cursor Settings로 접근하면 됨
2. Cursor는 .cursurignore 기능이 있어서, 테스트 케이스 파일을 편집할 수 없게 할 수 있음
3. .cursor 폴더 안에…
------------------------------
https://news.hada.io/topic?id=20595&utm_source=googlechat&utm_medium=bot&utm_campaign=1834

1

이이이얏호우우우!

가자! 댓글 없는 청정 사회로!

フトスト!

3

오픈 엑세스에 올라와 있는 논문들 중 소프트웨어 공학과 관련된 내용들을 편집하여 책으로 낸 것이다. 원문이 논문이라 그런지 몰라도 주장이 그렇게 혁신적이거나 새로운 것은 없다 다만 연구를 통해서 본인들의 주장에 대한 근거를 확보했다는 것이 유의미하다. 바꿔 말해 이 책에서 말하는 것들은 믿고 따라도 되는 어느 정도의 과학적 근거가 있는 이야기들.

소프트웨어 엔지니어링 생산성 돌아보기
aladin.kr/p/7zTVn

0

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

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

9
1

Rust로 작성한 JPEG XL 디코더, jxl-oxide의 버전 0.12.0을 릴리스했습니다. https://github.com/tirr-c/jxl-oxide/releases/tag/0.12.0

CMYK 프로파일 등 복잡한 ICC 프로파일을 지원하기 위해 기존에 사용하던 Little CMS 2 (lcms2) 에 더해, Rust로 작성된 색 관리 시스템인 moxcms 지원을 추가한 것이 주요 변경사항입니다. CLI 툴의 기본 CMS는 아직 lcms2이지만 --cms moxcms 옵션으로 moxcms를 사용할 수 있습니다.

9

Excited to share that we just released ESMeta v0.6.0! Here's two new features that we're really excited about in this release. Since this is my personal account, I won't be introducing the entire toolchain, but if you're curious, check out on https://github.com/es-meta/esmeta/ (1/n)

2

Jaeyeol Lee shared the below article:

간단한 rpmbuild의 사용과 private rpm package 배포

Perlmint @perlmint@hackers.pub

마지못해 패키지를 만들어야 할 것 같은 사람을 위한 설명입니다. 제대로된 패키지를 만들고 싶은 경우에는 부족한 점이 많습니다.

대부분의 경우에는 프로그램을 직접 소스에서 빌드하는 일도 적고, 그걸 시스템 전역에 설치하는 일도 흔치는 않을 것입니다. 좋은 패키지매니저와 관리가 잘되는 패키지 저장소들을 두고 아주 가끔은 직접 빌드를 할 일이 생기고, 흔치 않게 시스템 전역에 설치할 일이 생길 수 있습니다. 어지간한 프로그램들은 요즈음의 장비에서는 별 불만 없이 빌드 할 만한 시간이 소요되나, 컴파일러처럼 한번 빌드했으면 다시는 하고 싶지 않은 프로그램을 다시 설치해야하는 경우도 있을 수 있습니다. 하필 이런 프로그램들은 결과물도 덩치가 매우 큽니다. 이럴 때는 최대한 간단하고 필요한 항목만 패키지에 넣어서 만들어두고 다시 활용하면 좋을 것이기에 이런 경우를 위한 rpmbuild에 대한 나름 최소한의 사용 방법을 정리해봅니다.

rpmbuild

rpmbuild는 rpm-build 패키지로 설치가 가능하며, 나름 단순하게 rpm으로 패키징을 할 수 있는 유틸리티입니다. spec파일에 패키지 정보, 빌드 명령, 설치 명령, 패키지가 포함해야 할 파일 목록을 작성해서 rpmbuild에 입력으로 넣어주면 빌드부터 시작해서 rpm패키지를 만들어줍니다. native 프로그램의 경우 디버그 심볼을 알아서 분리해서 별도의 패키지로 만들어주고, 필요한 의존성도 추정해서 명시해줍니다. 또한, 필요한 경우 하나의 spec 명세로 연관된 서브 패키지도(ex. 실행파일 패키지인 curl과 라이브러리 패키지 libcurl, 라이브러리를 사용하기 위한 개발 패키지 libcurl-devel) 같이 만들 수 있습니다.

작업 환경

rpmbuild는 기본으로 ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS,BUILDROOT}의 경로에서 동작하며 각 경로의 용도는 다음과 같습니다.

  • SOURCES에는 압축된 소스코드가 위치합니다.
  • SPECS에는 패키지 정의인 spec파일을 둡니다.
  • BUILD밑에서 빌드 작업이 진행됩니다.
  • RPMS에 바이너리 rpm결과물이 생성됩니다.
  • SRPMS에는 소스 rpm결과물이 생성됩니다.
  • BUILDROOT는 패키징 하기 위해 빌드 결과물을 모으는 경로입니다.

spec파일

spec파일은 패키지를 어떻게 빌드하고 어떤 항목들이 패키지에 포함될지, 패키지의 이름, 설명 및 의존성 등의 메타데이터, 패키지 설치, 삭제시의 스크립트를 정의할 수 있습니다. 보통 시작 부분에는 메타데이터 정의로 시작하며, 다음과 같은 기본적인 형태를 취합니다. 나름 단순하게 만든 python을 위한 spec을 예시로 들어보겠습니다.

Summary: Python %{version}
Name: python-alternative
Version: %{version}
Release: 1%{?dist}
Obsoletes: %{name} <= %{version}
Provides: %{name} = %{version}
URL: https://www.python.org
Requires: libffi openssl
AutoReq: no
License: PSFL

Source: https://www.python.org/ftp/python/%{version}/Python-%{version}.tgz

BuildRequires: libffi-devel openssl-devel
BuiltRoot: %{_tmppath}/%{name}-%{version}-%{release}-root

%define major_version %(echo "%{version}" | sed -E 's/^([0-9]+)\\..+/\1/' | tr -d)
%define minor_version %(echo "%{version}" | sed -E 's/^[0-9]+\\.([0-9]+)\\..+/\1/' | tr -d)

%description
Python

%package devel
Summary: python development files
Requires: %{name} = %{version}-%{release}

%description devel
Python development package

%prep
%setup -q -n Python-%{version}

%build
./configure --prefix=%{_prefix}

%install
%{__make} altinstall DESTDIR=%{buildroot}
%{__ln_s} -f %{_bindir}/python%{major_version}.%{minor_version} %{buildroot}/%{_bindir}/python%{major_version}

%clean
%{__rm} -rf %{buildroot}

%files
%{_bindir}/python*
%exclude %{_bindir}/idle*
%{_bindir}/pip*
%{_bindir}/pydoc*
%exclude %{_bindir}/2to3*
%{_libdir}/libpython*
%{_prefix}/lib/libpython*
%{_prefix}/lib/python*
%{_mandir}/man1/python*

%files devel
%{_includedir}/python*
%{_prefix}/lib/pkgconfig/python*

%로 매크로를 사용할 수 있으며, %package, %description, %files 같은 매크로는 인자를 주어서 서브 패키지를 정의하는데도 쓸 수 있습니다. 앞선 예제처럼 devel 이라고작성하면 메인 패키지이름 뒤에 붙여서 python-alternative-devel가 되며, curl - libcurl과 같은 경우에는 메인의 이름은 curl이고, 딸린 패키지를 정의할 때는 %package -n libcurl과 같이 -n옵션을 추가해서 지정할 수 있습니다. 몇몇 매크로는 단계를 정의하는 것과 같은 동작을 하며 다음과 같습니다.

%package

유사성을 보면 spec파일의 맨 첫부분은 메인 패키지의 %package에 해당하는 것이 아닌가 싶습니다. <Key>: <Value>의 형태로 메타정보를 작성합니다. 대부분은 Key를 보면 무슨 값인지 추측 할 만합니다. 나중에 설명할 %files에서 나열한 파일을 rpmbuild가 분석하여 자동으로 패키지가 필요로 하는 의존성을 추정해서 추가 해 줍니다. python 스크립트, perl 스크립트, native 실행파일 등을 분석해서 알아서 추가해주는 것 같은데, 경우에 따라서는 틀린 의존성을 추가해주기도 합니다. 이 때는 AutoReq: no를 설정하여 자동 의존성 추가를 막을 수 있습니다. 이 python-alternative 패키지는 /usr/local/bin/python%{version}을 설치하는데 아마도 같이 포함되는 python 스크립트에 의해서 /bin/python을 의존성으로 추정하여 요구합니다. 패키지 스스로가 제공하는 의존성은 미리 설치 되어있기를 요구하지 않게 동작하는 것 같으니 보통은 문제가 없습니다만, 이 경우에는 스스로 제공을 하지 않기 때문에 python을 설치하기 위해서 python이 필요한 경우가 발생하므로 AutoReq를 껐습니다.

%prep

준비단계로 소스코드의 압축을 해제하고 필요한경우 패치를 적용합니다. %setup 매크로를 이 안에서 보통 사용하며, %setupSource에 명시된 파일명의 압축 파일을 SOURCES 밑에서 찾아서 압축을 풉니다. 그리고 동일한 이름의 디렉토리로 이동을 합니다. 앞선 예제에서는 SOURCES/Python-%{version}.tgz의 압축을 풀고 Python-%{version}으로 이동을 합니다. 패치가 필요한 경우 보통 이 뒤에 패치를 적용하는 명령들을 추가 합니다.

%build

설정, 컴파일 등을 수행하는 단계입니다. 이곳에서 자주 하는 매크로로 %configure, %make_build 등이 있습니다. %configure는 configure를 prefix 및 기타 몇가지 일반적으로 쓰이는 옵션을 추가하여 실행해주며, %make_buildmake와 비슷하게 모든 타겟을 빌드 합니다. 예제에서는 둘다 안쓰고 있고, 심지어 실제 빌드는 안하는데 어쨌든 이후의 %install까지 지나고나서 빌드 결과물만 맞는 위치에 만들어지면 대충 패키지를 만드는데는 별 문제는 없는 것 같습니다.

%install

여기서 빌드 결과물을 설치하는 명령을 작성합니다. 일반적으로 %make_install을 사용하여 make install DESTDIR=%{buildroot}와 비슷한 명령을 수행하여 %{buildroot}밑에 빌드 결과물이 prefix를 유지하여 설치되게 합니다. 예제의 %{__ln_s} -f %{_bindir}/python%{major_version}.%{minor_version} %{buildroot}/%{_bindir}/python%{major_version}을 보면 추정 할 수 있듯이, 패키지에 포함시킬 파일들을 %{buildroot}밑에 생성을 하면 되며, 추가적인 심볼릭 링크는 패키지를 빌드하는 시점에는 존재하지 않지만, 패키지를 설치하게되면 존재하게 될 %{_bindir}/python%{major_version}.%{minor_version}를 향하는 것을 %{buildroot} 밑인 %{buildroot}/%{_bindir}/python%{major_version}에 만듭니다.

%files

패키지에 포함될 파일 목록을 작성합니다. glob 양식으로 파일 목록을 작성할 수 있습니다. %{buildroot} 밑에 생성 되었지만 어느 %files에도 포함되지 않은 파일이 있는 경우에는 빌드를 실패합니다. 그러므로 %exclude를 사용해서 명시적으로 제외해줘야 합니다.

기타 매크로

rpmbuild에서는 기본으로 다양한 매크로를 제공하고 있습니다. --define "_libdir %{_prefix}/lib64"와 같은 옵션을 실행시에 주어서 실행시점에 매크로를 덮어 쓸 수도 있고, 앞선 spec파일 내의 %define major_version 와 같이 다른 매크로와 셸 명령을 활용하여 매크로를 정의 할 수도 있습니다. 원하는 동작을 안하는 것 같은 경우에는 --show-rc옵션을 사용하여 매크로가 어떻게 정의되어있는지 확인해 볼 수 있습니다.

빌드

rpmbuild의 매뉴얼을 보면 자세하게 나와있지만 가장 단순하게는 rpmbuild -bb <specfile>로 바이너리 패키지를 빌드할 수 있습다. 이 때, 압축된 소스코드는 미리 SOURCES밑에 두어야 합니다.

private rpm package 배포

직접 비공개 패키지 저장소 프로그램을 실행하여 제공하는 방법도 있겠지만, 최대한 간단하게 할 수 있는 방법으로, rpm관련 패키지 설치 명령이 입력으로 http등의 URL도 받는 것을 활용하여 적당한 장비에서 http로 서빙을 해주면 됩니다.

Read more →
4

간만에 필받아서 aider 커꾸(commit 꾸미기) 함.
gum 이라는 훌륭한 쉘꾸 도구 + git pretty format + delta 썼어요

# Define the git log format string with color formatting for better readability
local GIT_FORMAT="%C(bold yellow)Hash:%C(reset) %C(bold cyan)%h%C(reset) %C(dim white)(%cd)%C(reset)%n"
GIT_FORMAT+="%C(bold yellow)Author:%C(reset) %C(bold white)%an%C(reset) %C(dim white)<%ae>%C(reset)%n"
GIT_FORMAT+="%C(bold yellow)Message:%C(reset) %C(bold white)%s%C(reset)"

# Define the date format
local DATE_FORMAT="%Y-%m-%d %H:%M:%S"

# Perform the commit with aider and show a styled commit summary
aider --commit && \
gum style \
  --border rounded \
  --padding "0 2" \
  --border-foreground 39 \
  "$(git log -1 \
      --pretty=format:"$GIT_FORMAT" \
      --date=format:"$DATE_FORMAT" \
      --color=always)" && \

# Show detailed changes using delta for side-by-side diff with line numbers
git show -1 --color=always --stat --patch | delta --side-by-side --line-numbers
2

🤔

Protocols such as ActivityPub are widely used and useful, but unfortunately are not the best option when efficiency is important. Messages are in plain JSON format, which is wasteful, and extensions by various implementations complicate the implementation.

XQ's focus on replacing JSON with Protocol Buffers seems misguided. While serialization efficiency matters, ActivityPub's fundamental bottlenecks are in its multi-hop network architecture and request patterns. Optimizing message format without addressing these core architectural inefficiencies is like polishing doorknobs on a house with structural issues. True performance gains would require rethinking the communication model itself.

https://github.com/misskey-dev/xq

3
0
0

DuckDB 인 액션 - SQL만 알면 되는 로컬 데이터 분석, DuckDB로 가볍게 시작한다 (마크 니드햄, 마이클 헝거, 마이클 시몬스 (지은이), 김의윤 (옮긴이) / 인사이트 / 2025-05-07 / 28,000원) feed.kodingwarrior.dev/r/qmINu

aladin.co.kr/shop/wproduct.asp

0

내가 제일 좋아하는 프로그래밍 문법, "파이프라이닝"
------------------------------
- *파이프라이닝* 은 프로그래밍 언어에서 코드의 가독성과 유지보수성을 높이는 중요한 기능임
- *데이터 흐름을 왼쪽에서 오른쪽, 위에서 아래로 자연스럽게* 표현할 수 있게 해주는 방식
-
Rust 와 같은 언어에서 파이프라이닝은 코드의 흐름을 명확하게 하고, IDE 의 자동 완성 기능을 통해 개발 생산성…
------------------------------
https://news.hada.io/topic?id=20473&utm_source=googlechat&utm_medium=bot&utm_campaign=1834

0

랭체인으로 RAG 개발하기 : VectorRAG & GraphRAG - 현직 AI Specialist에게 배우는 RAG! 파이썬, 오픈AI, Neo4j로 실습하며 개념과 원리를 이해하고, 오픈AI와 딥시크 비교 분석까지! (서지영 (지은이) / 길벗 / 2025-04-25 / 29,000원) feed.kodingwarrior.dev/r/9pIEy

aladin.co.kr/shop/wproduct.asp

0

미래의 기술은 'AI'가 아니라 '집중력'임
------------------------------
- LLM은 반복 작업 자동화와 브레인스토밍 등에 유용하지만, *맹목적인 의존은 문제 해결 능력 저하를 초래할 수 있음*
- 특히 *새로운 문제에 대한 LLM의 신뢰도는 낮아* , 인간 엔지니어의 판단력이 중요함
- 구글 같은 검색엔진은 탐색과 활용의 균형을 제공하지만,
LLM은 즉시 ‘활용’(exploitation)만 유…
------------------------------
https://news.hada.io/topic?id=20458&utm_source=googlechat&utm_medium=bot&utm_campaign=1834

0

Vibe 코딩은 저품질 작업에 대한 변명이 될 수 없어요
------------------------------
- *AI 기반 바이브 코딩* 은 혁신적이지만, *품질 없는 속도는 위험* 하다는 경고의 글

"더 빨리 움직이고, 더 많이 망가뜨려라"
"vibe coding, 두 명의 엔지니어가 50명의 기술 부채를 만들어낼 수 있는 방식"
- 이 실리콘밸리의 오래된 슬로건을 비튼 표현은 최근 엔지니어링 커뮤니티에서 “vibe coding…
------------------------------
https://news.hada.io/topic?id=20449&utm_source=googlechat&utm_medium=bot&utm_campaign=1834

0

터미널에 대해 궁금한 점이 생겨 자료를 찾다 보니 Windows Console Team에서 연재한 시리즈물을 발견했는데, 그 내용이 참 유익했다.

Windows Command-Line Series:

  1. Backgrounder
  2. The Evolution of the Windows Command-Line
  3. Inside the Windows Console
  4. Introducing the Windows Pseudo Console (ConPTY)
  5. Unicode and UTF-8 Output Text Buffer

콘솔 앱이 터미널과 입출력을 주고 받는 것을 공기처럼 당연하다고 생각했는데, 그 과정에는 커서 이동이나 개행 등과 같은 제어 문자를 렌더링하거나 SIGINT 같은 시그널을 발생시켜주는 처리가 존재했다. 터미널과 콘솔 앱이 서로 분리된 구조가 과거 물리 터미널로부터 비롯된 것도 흥미로웠고 말이다. 사실 이해 못 한 부분이 아직 많아서 다음에 또 읽어볼 생각이다.

6
0
0

배그로 설명하는 수학, 실험, 과학의 관계

수학: 배그에서 총을 쏠 때 총알 궤적
실험: 현실에서 총을 쏘면 배그와 동일한 총알 궤적이 나올까? 궁금한데? 쏴서 비교해보자!
과학: 실험 몇 번 해보니까 배그랑 현실이랑 총알 궤적이 같더라! 앞으로 총알 궤적 예측할 때는 배그켜서 확인하자!

0
0

가장 선호하는 JetBrains IDE가 AI 시대에 뒤쳐지고 있어서 안타까웠는데 AI assistant 와 Junie 업데이트로 이제 좀 쓸만해진 것 같다.

여전히 부족한 점이 많기는 하다.
Agent는 느리고, 현재 상태에 대한 가시성이 없어 계속 기다려야할지 중단하고 새로운 세션을 열어야 할지에 대한 판단이 안선다.

prompt를 별도 관리할 수 있게 한 점은 훌륭하나 포맷이나 디렉토리를 유저가 선택할 수 있게 했더라면 더욱 유용했을 것이다. 나는 prompt가 다른 에이전트와 공유 가능하길 원한다.

vscode copilot처럼 Claude로부터 mcp 서버 설정을 불러올 수 있다. 하지만 역시 현재 상태 가시성이 없어 제대로 mcp 서버와 인터랙션이 되고 있는지 확인하기 어렵다.

그럼에도 불구하고 Cursor 나 Copilot에 충분히 대항할만한 업데이트라 생각한다. 앞으로를 응원한다!
https://www.jetbrains.com/ko-kr/junie/

1
0
1

Jaeyeol Lee shared the below article:

함수형 언어의 평가와 선택

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

함수형 언어(Functional Language)의 핵심

함수형 언어가 점점 많은 매체에 노출되고, 더 많은 언어들이 함수형 언어의 특징을 하나 둘 받아들이고 있다. 함수형 언어, 적어도 그 특징이 점점 대세가 되고 있다는 이야기이다. 하지만, 함수형 언어가 대체 무엇이란 말인가? 무엇인지도 모르는 것이 대세가 된다고 할 수는 없지 않은가?

함수형 언어란 아주 단순히 말해서 함수가 표현식[1]인 언어를 말한다. 다른 말로는 함수가 이기 때문에 다른 함수를 호출해서 함수를 얻어내거나 함수의 인자로 함수를 넘길 수 있는 언어를 말한다. 그렇다면 이 단순화된 핵심만을 포함하는 언어로 함수형 언어의 핵심을 이해할 수 있지 않을까? 이게 바로 람다 대수(Lambda Calculus)의 역할이다.[2]

람다 대수는 딱 세 종류의 표현식만을 가지고 있다.

  1. 변수 (xx, yy, …\ldots)
  2. 매개변수 xx에 인자를 받아 한 표현식 MM(함수의 몸체)을 계산하는 함수 (λx→M\lambda x\to M)
  3. 어떤 표현식 LL의 결과 함수를 인자 NN으로 호출 (L NL\ N)

이후의 설명에서는 MMNN, 그리고 LL이라는 이름을 임의의 표현식을 나타내기 위해 사용할 것이다. 람다 대수가 어떤 것들을 표현할 수 있는가? 앞에서 말했듯이 람다 대수는 함수의 인자와 함수 호출의 결과가 모두 함수인 표현식을 포함한다. 예를 들어 λx→(λy→y)\lambda x \to (\lambda y \to y) 는 매개변수 xx에 인자를 받아 함수 λy→y\lambda y \to y를 되돌려주는 함수이고, λx→(x (λy→y))\lambda x \to (x\ (\lambda y \to y))는 매개변수 xx에 함수인 인자를 받아 그 함수를 (λy→y\lambda y \to y를 인자로 사용하여) 호출하는 함수이다.

람다 대수(Lambda Calculus)의 평가(Evaluation)

이제 문제는

그래서 람다 대수의 표현식이 하는 일이 뭔데?

이다. 위의 표현식에 대한 소개는 산수로 말하자면 x+yx + y와 같이 연산자(++)와 연산항(xxyy)로부터 얻어지는 문법만을 설명하고 있고, 3+53 + 5와 같은 구체적인 표현식을 계산해서 88이라는 결과 값을 내놓는 방식을 설명하고 있지 않다. 이런 표현식으로부터 값을 얻어내는 것을 언어의 "평가 절차"("Evaluation Procedure")라고 한다. 람다 대수의 평가 절차를 설명하는 것은 어렵지 않다. 적어도 표면적으로는 말이다.

  • 함수는 이미 값이다.
  • 함수 λx→M\lambda x \to MNN으로 호출하면 MM에 등장하는 모든 xxNN으로 치환(Substitute)하고 결과 표현식의 평가를 계속한다.

이는 겉으로 보기에는 말이 되는 설명처럼 보인다. 하지만 이 설명을 실제로 해석기(Interpreter)로 구현하려고 시도한다면 이 설명이 사실 여러 세부사항을 무시하고 있다는 점을 깨닫게 될 것이다.

  1. 함수 호출 L NL\ N에서 LL이 (아직) λx→M\lambda x \to M 꼴이 아닐 때는 어떻게 해야하지?
  2. 함수 호출 (λx→M) N(\lambda x \to M)\ N에서 NN을 먼저 평가하는 게 낫지 않나? xxMM에 여러번 등장한다면 NN을 여러번 평가해야 할텐데?

첫번째 문제는 비교적 간단히 해결할 수 있다. LL을 먼저 평가해서 λx→M\lambda x \to M 꼴의 결과 값을 얻어낸 뒤에 호출을 실행하면 되기 때문이다. 반면에 두번째 질문은 좀 더 미묘한 문제를 가지고 있다. 함수 호출의 평가에서 발생하는 이 문제에 구체적인 답을 하기 위해서는 값에 의한 호출(Call-By-Value, CBV)와 이름에 의한 호출(Call-By-Name, CBN)이 무엇인지 이해해야 한다.

값에 의한 호출(Call-By-Value)? 이름에 의한 호출(Call-By-Name)?

앞에서 말한 함수 호출에서부터 발생하는 문제는 사실 함수형 언어에서만 발생하는 문제는 아니다. C와 같은 명령형 언어에서도 함수를 호출할 때 인자를 먼저 평가해야하는지를 결정해야하기 때문이다. 즉 이 문제는 함수를 가지고 있고 함수를 호출해야하는 모든 언어들이 가지고 있는 문제이다.

그렇다면 이 일반적인 문제를 어떻게 해결하는가? 대부분의 언어가 취하는 가장 대표적인 방식은 "값에 의한 호출"("Call-By-Value", "CBV")이라고 한다. 이 함수 호출 평가 절차에서는 함수의 몸체에 인자를 치환하기 전에[3] 인자를 먼저 평가한다. 이 방식을 사용하면 인자를 여러번 평가해야하는 상황을 피할 수 있다.

또 다른 방식은 "이름에 의한 호출"("Call-By-Name", "CBN")이라고 한다. 이 방식에서는 함수의 몸체에 인자를 우선 치환한 후 몸체를 평가한다. 몇몇 언어의 매크로(Macro)와 같은 기능이 이 방식을 사용한다. 얼핏 보기에는 CBN은 장점이 없어보인다. 그러나 함수가 인자를 사용하지 않을 경우는 CBN이 장점을 가진다는 것을 볼 수 있다. 극단적으로 평가가 종료되지 않는 표현식(Non-terminating expression)이 있다면[4] CBV는 종료하지 않고 CBN만이 종료하는 경우가 있음을 다음 예시를 통해 살펴보자. 표현식 (λx→(λy→y)) N(\lambda x \to (\lambda y \to y))\ N이 있다고 할 때, NN이 평가가 종료되지 않는 표현식이라고 하자. 이 경우 CBV를 따른다면 종료하지 않는 NN 평가를 먼저 수행하느라 이 표현식의 값을 얻어낼 수 없지만, CBN을 따른다면 λy→y\lambda y \to y라는 값을 손쉽게 얻어낼 수 있다. 바로 이런 상황 때문에

CBN은 CBV보다 일반적으로 더 많은 표현식들을 평가할 수 있다

고 말한다.

모호한 선택을 피하는 방법

두 방식의 장점을 모두 가질 수는 없을까? 다시 말해서, 어떤 상황에서는 이름에 의한 호출을 사용하고, 어떤 상황에서는 값에 의한 호출을 사용할 수 없을까? 이 질문에 답한 수많은 선구자들 가운데 폴 블레인 레비(Paul Blain Levy)가 내놓은 답인 "값 밀기에 의한 호출"("Call-By-Push-Value", "CBPV")은 함수형 언어의 평가를 기계 수준(Machine level)에서 이해하는데에 있어 강력한 도구를 제공한다. CBPV는 우선 "계산"("Computation")과 "값"("Value")을 구분한다.

  • 계산 MM, NN, LL, …\ldots = 함수 λx→M\lambda x \to M 또는 함수 호출 L VL\ V
  • VV, UU, WW, …\ldots = 변수 xx

잠깐, 앞서서 함수형 언어에서 함수는 값이라고 하지 않았던가? 이는 값 밀기에 의한 호출에서 함수와 함수 호출을 종전과 전혀 다르게 이해하기 때문이다. 함수 λx→M\lambda x \to M는 스택(Stack)에서 값을 빼내어(Pop) xx라는 이름을 붙인 후 MM을 평가하는 것이고, 함수 호출 L VL\ V는 스택에 값 VV를 밀어넣고(Push)[5] LL을 평가하는 것이다. 따라서 함수 λx→M\lambda x \to M는 평가의 결과가 아닌 추가적인 평가가 가능한 표현식이 된다. 이 구분을 간결하게 설명하는 것이 다음의 CBPV 표어이다.

값은 "~인 것"이다. 계산은 "~하는 것"이다.

그렇지만 함수형 언어이기 위해서는 함수를 값으로 취급할 수 있어야 한다고 했지 않은가? 그렇다. 이를 위해 CBPV는

계산을 강제한다면(force\mathbf{force}) 계산 MM를 하는 지연된 계산인 값 thunk(M)\mathbf{thunk}(M)

을 추가로 제공한다. 이 둘 (force(V)\mathbf{force}(V)thunk(M)\mathbf{thunk}(M))을 다음과 같이 문법에 추가할 수 있다.

  • 계산 = λx→M\lambda x \to M 또는 L VL\ V 또는 force(V)\mathbf{force}(V)
  • 값 = xx 또는 thunk(M)\mathbf{thunk}(M)

CBPV를 완성하기 위해 필요한 마지막 조각은 계산을 끝내는 법이다. 현재까지 설명한 λx→M\lambda x \to ML VL\ V 그리고 force(V)\mathbf{force}(V) 는 모두 다음 계산을 이어서 하는 표현식이고, 계산을 끝내는 방법을 제공하지는 않는다. 예를 들어 λx→M\lambda x \to M의 평가는 스택에서 값을 빼내고 계산 MM의 평가를 이어한다. 그렇다면 계산의 끝은 무엇인가? 결과 값을 제공하는 것이다. 이를 위해 return(V)\mathbf{return}(V)를 계산에 추가하고, 이 결과 값을 사용할 수 있도록 M to x→NM\ \mathbf{to}\ x \to N (계산 MM을 평가한 결과 값을 xx라고 할 때 계산 NN을 평가하는 계산) 또한 계산에 추가하면 다음의 완성된 CBPV를 얻는다.

  • 계산 = λx→M\lambda x \to M 또는 L VL\ V 또는 force(V)\mathbf{force}(V) 또는 return(V)\mathbf{return}(V) 또는 M to x→NM\ \mathtt{\mathbf{to}}\ x \to N
  • 값 = xx 또는 thunk(M)\mathbf{thunk}(M)

이제 CBPV를 얻었으니 원래의 목표로 돌아가보자. 어떻게 CBV 호출과 CBN 호출을 CBPV로 설명할 수 있을까?

  • CBV 함수 λx→M\lambda x \to M와 호출 L NL\ N이 있다면, 이를 return(thunk(λx→M))\mathbf{return}{(\mathbf{thunk}(\lambda x \to M))}L to x→N to y→force(x) yL\ \mathbf{to}\ x \to N\ \mathbf{to}\ y \to \mathbf{force}(x)\ y로 표현할 수 있다. 즉, CBPV의 관점에서 CBV의 함수는 지연된 원래 계산 λx→M\lambda x \to M을 값으로 되돌려주는 계산으로 이해할 수 있고, 함수 호출 L NL\ N은 함수 부분 LL을 먼저 평가하고 NN을 평가한 뒤 NN의 계산 결과 yy를 스택에 밀어넣고 지연된 계산인 함수 부분 xx의 계산을 강제하는(force(x)\mathbf{force}(x)) 것으로 이해할 수 있다.
  • CBN 함수 λx→M\lambda x \to M와 호출 L NL\ N이 있다면, 이를 λx→M\lambda x \to M(단, 변수 xx의 모든 사용을 force(x)\mathbf{force}(x)로 치환함)과 L thunk(N)L\ \mathbf{thunk}(N)로 표현할 수 있다. 즉, CBPV의 관점에서 함수 호출은 L NL\ N은 지연된 NN을 스택에 밀어넣은 뒤 LL의 계산을 이어가는 것으로 볼 수 있다. 이 지연된 NN은 이후에 스택에서 빼내어져 어떤 이름 xx가 붙은 뒤, 이 변수가 사용될 때에야 비로소 계산된다.

다소 설명이 복잡할 수 있으나, 단순하게 말해서 CBPV는 CBV에 따른 상세한 평가 순서와 CBN 따른 상세한 평가 순서를 세부적으로 설명할 수 있는 충분한 기능을 모두 갖추고 있으며, 이를 통해 CBV 함수 호출과 CBN 함수 호출을 모두 설명할 수 있다는 이야기이다.

기계 수준(Machine level)에서의 Call-By-Push-Value의 장점

앞에서는 CBPV가 CBV와 CBN를 모두 설명할 수 있음을 다뤘다. 그러나 CBPV는 프로그래머(Programmer)가 직접 사용하기에는 과도하게 자세한 세부사항들을 포함하고 있기에, 프로그래머가 직접 CBPV를 써서 CBV와 CBN의 구분을 조율하기에는 적합하지 않다. 그렇다면 어느 수준에서 CBV와 CBN을 혼합해 사용할 때 도움을 줄 수 있을까? 바로 람다 대수를 기계 수준으로 컴파일(Compile)할 때이다. 이때는 CBPV가 가진 자세한 세부사항의 표현력이 굉장히 유용해진다.

예를 들어 람다 대수를 기계 수준으로 변환할 때 흔히 필요한 것 중 하나인 항수 분석(Arity analysis)에 대해 이야기해보자. 항수 분석은 함수가 하나의 인자를 받은 뒤 실행되어야 하는지, 혹은 두 인자를 모두 받아 실행되어야 하는지 등을 확인하여 이후에 그에 걸맞는 최적화된 기계어(Machine language)를 생성할 수 있게 도와주는 분석 작업이다. 평범한 람다 대수에서는 항수 분석의 결과를 직접적으로 표현하기 어렵다. 예를 들어 람다 대수의 λx→(λy→y)\lambda x \to (\lambda y \to y)의 경우 이 함수가 xxyy를 모두 받아 yy를 되돌려주는 함수인지 (항수가 2인 함수인지), 혹은 xx를 받아 λy→y\lambda y \to y라는 함수를 되돌려주는 함수인지 (항수가 1인 함수인지) 구분할 수 없다. 그러나 이를 CBPV로 변환한 λx→(λy→return(y))\lambda x \to (\lambda y \to \mathtt{return}(y))λx→return(thunk(λy→return(y)))\lambda x \to \mathtt{return}(\mathtt{thunk}(\lambda y \to \mathtt{return}(y)))는 각각이 무엇을 뜻하는지 분명히 이해할 수 있다.

  • λx→(λy→return(y))\lambda x \to (\lambda y \to \mathtt{return}(y))는 두 변수 xxyy를 스택에서 빼낸 뒤 yy의 값을 되돌려주는 함수(항수가 2인 함수)이다.
  • λx→return(thunk(λy→return(y)))\lambda x \to \mathtt{return}(\mathtt{thunk}(\lambda y \to \mathtt{return}(y)))는 변수 xx를 스택에서 빼낸 뒤 지연된 계산 λy→return(y)\lambda y \to \mathtt{return}(y)를 돌려주는 함수(항수가 1인 함수)이다.

이런 장점을 바탕으로 CBPV를 더 발전시킨 "언박싱한 값에 의한 호출"("Call-By-Unboxed-Value")을 GHC 컴파일러의 중간 언어(Intermediate language)로 구현하는 것에 대한 논의가 현재 진행되고 있으며 앞으로 더 많은 함수형 컴파일러들이 관련된 중간 언어를 채용하기 시작할 것으로 보인다.

마치며

이 글에서는 함수형 언어의 핵인 람다 대수를 간단히 설명하고 람다 대수를 평가하는 방법에 대해서 다루어보았다. 특히 그 중 값 밀기에 의한 평가(Call-By-Push-Value, CBPV)가 무엇이며 CBPV가 다른 대표적인 두 방법(CBV, CBN)을 어떻게 표현할 수 있는지, 그리고 CBPV의 장점이 무엇인지에 대해서도 다루어 보았다. 이 글에서 미처 다루지 못한 중요한 주제는 CBPV를 기계에 가까운 언어로 번역해보는 것이다. 여기에서는 글이 너무 복잡해지는 것을 피하기 위해 제했으나, CBPV의 장점에서 살펴봤듯 이는 CBPV에 있어 핵심 주제 중 하나이기 때문에 이후에 다른 글을 통해서라도 이 주제를 소개할 기회를 가지고자 한다. 이 글이 CBPV에 대한 친절한 소개글이었기를 바라며 이만 줄이도록 하겠다.


  1. 결과 (Value)을 가지는 언어 표현을 말한다. 예를 들어 1+11 + 122라는 값을 가지는 표현식이지만 (JavaScript의) let x = 3;나 (Python의) def f(): ...은 그 자체로는 값이 없기 때문에 표현식이 아니다. ↩︎

  2. 다만 실제 역사에서는 람다 대수의 이해와 발견이 함수형 언어의 개발보다 먼저 이루어졌다. 이런 역사적 관점에서는 (이미 많은 수학자들이 이해하고 있던) 람다 대수에 여러 기능을 추가한 것이 바로 함수형 언어라고 볼 수 있다. ↩︎

  3. 프로그래밍 언어(Programming Language)는 실제로는 치환을 사용하지 않고 환경(Environment)을 사용하는 경우가 더 많지만 설명의 편의를 위해 다른 언어들 또한 환경 대신 치환에 기반해 평가한다고 가정하겠다. ↩︎

  4. 앞서 설명한 람다 대수에서는 이를 쉽게 얻을 수 있다. 오메가(Ω\Omega)라고 부르는 표현식인 (λx→x x) (λx→x x)(\lambda x \to x\ x)\ (\lambda x \to x\ x)의 평가는 값에 의한 호출을 따르든 이름에 의한 호출을 따르든 종료되지 않는다. ↩︎

  5. 바로 이 함수 호출을 값 밀기에 기반해 해석하는 데에서 CBPV의 이름이 유래했다. ↩︎

Read more →
13
3
0

다른 분들이 여러 가지 말씀을 해 주셨습니다만 저도 첨언하자면,

"의업과 약업의 현실적 관계"도 한 가지 중대한 이유입니다. 제약회사 직원이 의사에게 굽실거리다 못해 예비군 훈련을 대신 가거나, 수술을 대신 한다는 기상천외한 뉴스 다들 한번쯤 보셨을 텐데요.

원론적으로는 의사가 약에도 빠삭해야 하지만, 현실적으로는 자기 전공분야도 너무 방대하고 약학도 너무 방대해서 그러기 어렵습니다. 마치 소프트웨어 엔지니어 중에 하드웨어 덕질까지 하는 경우는 소수이고 대부분은 그냥 맥북 사는 것과 비슷하게, 의사들의 약 지식도 한계가 있는 거죠. 어떤 약을 안 쓰는 게 무슨 이유가 있어서가 아니라 진짜로 그 약의 존재를 몰라서인 경우가 허다합니다. 그러니 약 성능 똑같아도 영업에 따라 억 단위가 왔다갔다 하고, 그러니 제약회사의 영업이 엽기뉴스의 영역으로 가는 것이죠.

이런 시장환경에서 의사들에게 약 이름과 성분 이름의 대조표를 매년 새로 외우라고 하면 망하겠죠? 그래서 어떻게든 이름만 보면 성분을 알게 하려고 발버둥치는 것입니다.

그러면 반대로 성분명과 전혀 무관한 약 이름은 어떻게 나오는지도 짐작이 되시죠? 그렇습니다. "처방전 필요없는" 약은 성분명 쿨하게 버리고 일반소비자에게 호소하는 작명을 하는 경향이 있습니다. 그리고, 처방전이 필요하더라도 동일성분의 약이 많거나 저네릭 경쟁이 벌어지는 경우에도 튀는 이름으로 차별화를 꾀하는 경향이 있죠.

RE:
https://serafuku.moe/notes/a6lapo16c2

0