Profile img

Hi, I'm who's behind Fedify, Hollo, BotKit, and this website, Hackers' Pub! My main account is at @hongminhee洪 民憙 (Hong Minhee) :nonbinary:.

Fedify, Hollo, BotKit, 그리고 보고 계신 이 사이트 Hackers' Pub을 만들고 있습니다. 제 메인 계정은: @hongminhee洪 民憙 (Hong Minhee) :nonbinary:.

FedifyHolloBotKit、そしてこのサイト、Hackers' Pubを作っています。私のメインアカウントは「@hongminhee洪 民憙 (Hong Minhee) :nonbinary:」に。

Website
hongminhee.org
GitHub
@dahlia
Hollo
@hongminhee@hollo.social
DEV
@hongminhee
velog
@hongminhee
Qiita
@hongminhee
Zenn
@hongminhee
Matrix
@hongminhee:matrix.org
X
@hongminhee

洪 民憙 (Hong Minhee) shared the below article:

`X-Frame-Options` 의 악몽에서 깨어나세요, 프록시 서버 개발기

소피아 @async3619@hackers.pub

X-Frame-Options 의 악몽에서 깨어나세요, 프록시 서버 개발기

X-Frame-Options? 그게 뭔가요?

우리가 아는 몇몇 대형 웹 서비스들(유튜브 등)은 보통의 경우 다른 웹 사이트에서 iframe 요소를 통해 임베딩 되는 것을 거부하지 않습니다. 다만, 몇몇 웹 서비스는 다른 웹 페이지에서 iframe 요소를 통해 표시되길 거부합니다. 제품 정책 및 보안상의 이유로 표시를 거부하는 목적이 있겠습니다.

이러한 니즈를 충족시킬 수 있는 HTTP 헤더가 X-Frame-Options 입니다. 이 헤더의 값을 SAMEORIGIN 내지는 DENY로 설정하면, 직관적인 값에 따라 알맞게 프레임 내 임베딩 가능 여부를 결정할 수 있습니다.

네이버 웹 사이트의 Response Header 목록, X-Frame-Options가 명시되어 있다.

위 이미지에서 볼 수 있듯, 네이버는 X-Frame-Options 헤더를 명시함으로써 그 어떠한 출처에서도 프레임 내에 임베딩 되는 것을 거부한 상황입니다. #

왜 이걸 우회하나요?

image

웹 페이지 내에 다른 웹 페이지가 임베딩 되어 '미리보기' 처럼 제공되는 경험을 보신 적이 있으신가요? Omakase AI는 자사 프로덕트의 데모를 위와 같이 제공하고 있습니다. 캡쳐하여 실시간으로 전송되는 영상의 화면 위에, 자사 컨텐츠를 올려두어 실제 적용 시에 어떤 네러티브를 제공할지 데모 형식으로 보여줍니다.

문제는 이 모든 경험이 영상을 통해 진행 된다는 점 입니다. 영상 전송은 필연적으로 지연 시간이 존재할 수 밖에 없습니다. 여러분이 스크롤을 내리고, 클릭을 하는 등의 작은 인터렉션 하나가, 큰 지연 시간 뒤에 처리가 된다고 하면 유저는 답답하고 매끄럽지 않음을 느낄 것이고 이는 곧 이탈로 이어질 가능성이 있습니다. (구글은 비슷한 맥락에서 Core Web Vitals로써 INP를 설명하고 있습니다)

따라서 저는 영상을 보내는 나이브한 방법 이외의 유저의 브라우저에서 외부 웹 서비스를 표시할 좋은 방법을 찾아야 했고, 그것이 바로 프레임 내지는 iframe을 사용하는 방법 이었습니다. 이런 맥락에서 X-Frame-Options를 우회할 필요가 생긴 것 입니다.

어떻게 했나요?

중간자의 필요성

image

우리는 정상적인 방법으로는 제 3자 출처의 요청을 가로채어 응답 데이터 및 헤더를 조작할 수 없음을 잘 알고 있습니다. 여기서 필요한게 중간자 (Man in the Middle) 입니다. 누군가를 클라이언트 - 원격지 서버 사이에 두어, 서로에게 오가는 요청과 응답을 수정하는 작업을 수행하도록 하는 것 입니다. 그렇게 하면, 둘은 각자 수신한 요청과 응답이 모두 원본인지, 수정한 것인지 알 수 있는 방법은 거의 없을 것 입니다.[1]

image

이 중간자 역할을 하는 프록시 서버를 중간에 두어 요청을 모두 프록시 서버를 거치도록 하는 방법을 이용하는 것 입니다. 간단하게는 X-Frame-Options 응답 헤더의 제거가 있을 것 입니다. 중간자가 X-Frame-Options 헤더를 제거함으로 응답을 수신하는 클라이언트 브라우저의 iframe 요소는 큰 문제 없이 내용을 표시할 수 있게 됩니다.

여기서 끝이 아니다. URL을 조작하기

처음에는 그저 GET Query Parameter로 프록시 서버에게 어떤 원격 URL을 프록시 할 것인지 명시하도록 구현 했습니다. 예를 들면 다음과 같은 형식이 될 수 있겠습니다:

https://example.com/proxy?target=https://www.naver.com/...

보통의 경우에는 별 문제 없이 동작 했습니다만, 재앙은 그다지 먼 곳에 있지 않았습니다. 만약 다음과 같은 코드가 원격지 웹 서비스 코드에 있다고 해봅시다. 아래 코드는 무신사 웹 페이지의 빌드된 소스코드 입니다:

import {E as k0} from "./vendor/react-error-boundary.js";
import {b as o0, d as Wt} from "./vendor/react-router.js";
import {d as F0} from "./vendor/dayjs.js";
import {L as Qt} from "./vendor/lottie.js";
import "./vendor/scheduler.js";
import "./vendor/prop-types.js";
import "./vendor/react-fast-compare.js";
import "./vendor/invariant.js";
import "./vendor/shallowequal.js";
import "./vendor/@remix-run.js";
import "./vendor/tslib.js";
import "./vendor/@emotion.js";
import "./vendor/stylis.js";
import "./vendor/framer-motion.js";
import "./vendor/motion-utils.js";
import "./vendor/motion-dom.js";

무신사는 내부적으로 ESM을 사용해서, import 구문을 통해 필요한 에셋을 불러오는 코드를 사용중에 있습니다. 문제는 여기서 발생합니다. import 의 대상이 되는 소스코드가 상대 경로를 따르게 되어 아래와 같은 결과를 초래하게 됩니다.

https://example.com/proxy?target=https://www.naver.com/...

위와 같은 URL을 표시하고 있는 `iframe` 요소에서,
`import "./vendor/framer-motion.js"` 구문을 만난다면..

https://example.com/proxy/vendor/framer-motion.js 를 요청하게 됨.

이를 해결하기 위해, 프록시 된 대상 URL에 대한 개념의 도입이 필요 했습니다. 상대 경로 진입에도 안정적으로 작동할 수 있는 새로운 방식의 접근이 필요 했습니다. 저는

  1. 상대 경로 접근에도 안전하며,
  2. 원본 URL의 구조를 그대로 남길 수 있는 구조

를 생각해 냈어야 했고, 그 결과는 이렇습니다. https://section.blog.naver.com/BlogHome.naver?directoryNo=0&currentPage=1&groupId=0 를 예시로 들면, 프록시화 (Proxified) 된 URL은 다음과 같은 것 입니다.

https://example.com/proxy/section/blog/naver/com/_/BlogHome.naver?...

URL hostname. 구분자를 /로 치환하고, 이후의 모든 pathname, search 등은 모두 _ 구분자 뒤로 넘김으로서 URL의 원형을 유지할 수 있게 됩니다. 추가적으로 상대 경로 접근에도 안전한 URL을 만들 수 있습니다.

최종 보스가 남아있다. HTML API 후킹하기

우리는 상대 경로 문제를 해결하기 위해 URL을 프록시화 하는 방법을 사용했고, 이는 제대로 동작하는 듯 해보였습니다. 악몽은 React, Vue 등의 SPA 웹 앱을 프록시하여 표시하는 데에서 시작 되었습니다.

React, Vue 와 같은 프레임워크들은 History API 및 window.location 객체를 기반으로 한 Routing 기능을 제공하고 있습니다.[2] 이 말은, 결국엔 어떤 프레임워크가 되었든 저수준 빌트인 자바스크립트 API를 사용할 수 밖에 없다는 것을 의미 합니다. 그렇다면 직관적으로 생각 해봤을 때,

window (및 globalThis) 객체의 location 속성의 값을 변경해주면 되지 않겠나?

라고 생각할 수 있습니다. 그러나 이는 불가능 합니다.

image

어떠한 이유 때문인지는 알 길이 없었지만, 자바스크립트는 그렇게 만만한 존재가 아니었습니다. 다른 좋은 방법을 찾아야 할 필요가 있었고, 결론에 도달하는 데에는 오랜 시간이 걸리지 않았습니다. 그것은 바로 원격지 웹 페이지에서 실행되는 모든 스크립트의 window.location 객체 접근을 감시하면 어떨지에 대한 아이디어 였습니다.

번들된 소스코드의 경우 대체적으로 다음과 같은 형식을 가지게 됩니다:

const l = window.location;
/* ... */ l.pathname /* ... */

여기서 우리는 변수 lwindow.location의 별칭인지 소스코드만 분석해서는 알기 매우 어렵습니다. 따라서, babel을 사용해서 소스코드를 AST로 분석하고, 모든 프로퍼티 접근을 특정 함수 호출로 변환하면, '특정 함수'에서 모든 것을 처리할 수 있으니 좋을 것 같다는 생각이 있었고, 실행에 옮겼습니다:

const l = window.location;
l.pathname;

// 위 코드는 아래와 같이 변환됨

const l = __internal_get__(window, 'location');
__internal_get__(l, 'pathname');

__internal_get__ 함수 내부에서 첫번째 인자가 window.location 과 동일한 인스턴스를 가지고 있는지 비교하거나, 두번째 인자인 프로퍼티 키를 비교해서 href 등의 값이라면, 원하는 값을 반환하도록 후킹 함수를 만들 수 있겠습니다.

function __internal_get__(owner, propertyKey) {
  if (owner === window.location) {
    return {
      get href() { /* proxified 된 url을 기반으로 Router를 속이는 URL을 반환하는 로직 */ }
    }
  }

  // ...
}

마치며

글에 열거한 내용 이외에도 정말 많은 기술이 사용 되었는데, 아주 재밌는 경험 이었습니다. 혹여나 이러한 비슷한 기능을 하는 기능을 개발할 일이 있으시다면, 도움이 됐으면 좋겠습니다.


  1. 여기서 거의 라는 표현을 사용한 이유는, 제 짧은 식견에서 보자면 비슷한 맥락에서 사용하는 기법으로 integrity 속성이 있을 수 있겠습니다. ↩︎

  2. 예를 들면, location.pathname을 읽어 현재 Route가 어떤 Route인지 감지하는 등의 동작이 있겠음. ↩︎

Read more →
3

洪 民憙 (Hong Minhee) shared the below article:

2025 Q4 Review

Jaeyeol Lee @kodingwarrior@hackers.pub

이번 분기는 그렇게 기대는 많이 하지는 않았는데, 이것저것 벌려놓은 일들은 있었어서 이거라도 수습은 해야겠다라는 마음가짐은 했던 것 같다. 벌려놓은 일이 많았던 만큼 역시나 업보를 청산하느라 정신이 없는 나날의 연속이었고, 그러면서도 나름 만족스러운 하루를 보내고 있었다. 그러면서도 여러가지 좋은 소식도 생겼다. 기대했던 것들을 상회하는 좋은 기회를 누리고 있어서 요즘은 매일마다 도파민이 넘치고 있는 삶을 살고 있다. 자세한 얘기는...... 후술하도록 하겠다.

Timeline

이번에도 업보청산하느라 정신없는 나날을 보내고 있었고, 그러면서도 중요한 이벤트도 몇 가지 일어났다.

  • 2025-10-06 (일본 IT 회사와의 첫 캐주얼 면담) : 지난 분기 마지막 쯤에 PyCon JP에서 알게된 좋은 인연을 통해서, 일본 소재의 IT 회사에서 원격으로 캐주얼 면담할 기회를 가질 수 있었다. 듀오링고로 겨우 체득한 어설픈 일본어 구사 실력, 읽고 쓰는 쪽으로는 문제가 없지만 듣고 말하는 쪽으로는 부족한 영어 실력이었기 때문에 여러모로 걱정은 되었는데 한국인 직원 분이 중간에 통역을 해주신 덕분에 나에 대해서 어필할 수도 있고, 일본의 IT 회사는 어떤 분위기인지 알아가는 유익한 시간을 보낼 수 있었다.
    • 이 시점까지만 해도..... 일본으로 꼭 가고 싶은 생각이 들었던 것 같다. 한 6개월 정도는 길게 보고 준비해야겠다는 생각을 했을 정도니까.
  • 2025-10-15 (구직 시작) : OSSCA 프로그램도 슬슬 끝나가고 있었던 시점이어서, 이력서도 완성하고 구직을 시동걸기 시작했다. 이력서는 평소에 했던 것처럼 typst로 깎았는데, 2 column 구조로 어떻게든 공백을 채웠던 것 같다. 이 때 당시까지만 해도, Fedify 프로젝트 기여할 때 NestJS 미들웨어를 개발했던 경험을 살려서 NodeJS 백엔드 개발자 혹은 풀스택 개발자 지원해야겠다는 마음가짐을 가지고 있었던 것 같다. 경력은 사실상 망하긴 했지만 외부활동을 나름? 열심히 했던 덕분인지 생각했던 것보다 채워넣을 수 있는 내용들이 많았다. 한 10명 정도 되는 분들한테 이력서 피드백을 요청했었는데, 전부 긍정적인 평가를 받았다. 이력서는 여기서 볼 수 있다.
  • 2025-10-25 (브라우저 스터디 시작) : Fedify 팀에서의 인연 (그리고, 누군가의 팬클럽 디스코드)으로 알게된 사람들과 밑바닥에서부터 만드는 웹 브라우저 라는 책으로 스터디를 시작했다. 다들 프로그래밍에 어느 정도는 자신있는 사람들이었어서, 매 챕터마다 연습문제를 모두 풀어오는 하드코어한 스터디였다. 풀스택 엔지니어로서의 근무를 희망하는 사람으로서 브라우저의 동작 원리를 제대로 이해하지 않으면 안되겠다는 생각이 들었기 때문에, 구직을 성공하기 전까지는 누구보다 진도를 앞서서 진행하고 매주마다 연습문제를 전부 다 풀고 Hackers Pub에도 풀이글을 꾸준히 게시했던 것 같다. 구직을 성공하기 전까지는.....
    • 어떻게든 16챕터까지 전부 풀어서 이걸로 나만의 스토리를 만들고 싶었던 욕심은 있지만, 2025년 12월 31일 기준, 챕터 5까지만 진도를 끝내고 멈춘 상태인 지금 당장의 나로서는 지금 내가 하고 있는 일에 충실해야겠다는 판단이 들어서 당분간은 미루고 있다.
    • 책 자체는 웹 개발을 공부한다기보다는 브라우저를 진짜로 밑바닥에서부터 만들면서 이해하는 교재이다보니 프론트엔드 개발 비슷한걸 기대한 사람들에게는 약간 당혹감을 느낄 수도 있다. 첫장부터 **"소켓프로그래밍"**으로 직접 HTTP 프로토콜로 요청을 쏘고 HTML 응답을 읽어들여서 화면에 GUI 툴킷으로 어떻게든 뿌리는 **"데스크톱 어플리케이션"**을 만드는 것이기 때문이다. 같이 스터디했던 분의 표현을 빌리자면, 사실상 게임엔진을 만드는 것이랑 유사하다.
    • 코드를 직접 따라치는 것 만으로도 벅차다고 느낄 수도 있긴 한데, 연습문제를 푸는 것도 굉장히 해볼만한 도전과제라고 생각하고 있다. "여기 있는 연습문제를 모두 풀 수 있는 사람이라면 어딜 가도 먹히지 않을까?" 라는 생각도 들곤 하는데, 나 말고도 연습문제를 푸는 스터디를 했던 사람이라면 모두 공감할 것이다.
  • 2025-11-01 (OSSCA 성과 발표회) : 길다면 길었고, 짧다면 짧았던 OSSCA 일정이 끝이 났다. Fedify 팀을 대표해서 OSSCA 기간 동안 어떤 기여를 했는지 발표하는 시간을 가졌다. 여기저기 UbuCon Korea/PyCon Korea/NeovimConf.live 등등에서 발표해본 경험은 있었기 때문에, 내가 발표하는게 맞겠다는 생각이 들어서 자진해서 지원했다. 다만..... OSSCA 성과 발표회에서는 대본만 열심히 준비했고, 대본을 안 보고 발표를 할 수도 있다는 가능성을 생각하지 못해서 만족스러운 수준으로 발표하지는 못했다. OSSCA를 중심으로 오픈소스 기여하는 사람들을 어셈블해서 Hackers Pub을 부흥시키겠다는 원대한 계획이 실현되지는 못해서 아쉬운 감은 없지 않다.
  • 2025-11-04 (미국 스타트업에서의 Work trial 시작) : OSSCA 성과발표회가 있고 얼마 지나지 않아서, 어느날 갑자기 미국의 어떤 스타트업 CTO분한테서 커피챗 관련으로 연락이 왔다. 당장 미국으로 갈 계획까지는 없었지만, 어떻게든 진짜 구직을 해야겠다고 생각하고 있던 입장이었어서 가벼운 마음으로 커피챗을 했던 것 같다. 그리고, 그 다음날 CEO도 같이 껴서 역시 가볍게 커피챗을 진행했는데 커피챗이 끝나자마자? 바로 Work Trial을 시작했다. 매일마다 도파민에 찌든 삶을 살게 된 것은 이때부터가 시작이었다...
  • ⭐️ 2025-11-15 (Work trial 수습 통과) : 2주도 안되는 Work Trial이 진행되는 동안, 정말 많은 일들이 있었던 것 같다. 뭔가 좀 부족하다 싶은 것들을 발견할때마다 PR을 올리긴 했었는데, 진행하게 된 지 거의 3~5일 만에 PR을 많이 올렸다면서 샤라웃을 받기도 했었고, 수습 7일차되는 시점에 외부 시스템을 연동하는 등의 큰 기능을 맡게 되기도 했다. 그리고 12일차에 Work trial 수습 통과했고 정식적인 멤버로 같이 할 수 있게 되었다. 당장은 미국으로 건너갈 조건이 되지 않아서 파트타임으로 일하고 있긴 하지만, 지금까지 받아왔던 대우, 지금까지 근무했던 것보다 훨씬 더 좋은 조건으로 일하고 있어서 너무 만족스럽게 살고 있다. 구직을 본격적으로 시작한지 한 달 만에 자리 잡았다는 것 자체도 사실은 안 믿긴다.
    • 회사에 대해서 자랑을 좀 하자면.......
      1. 최고의 복지는 동료라고 했던가? 누구보다 제품개발에 진심이고 배울 점이 많은 사람들과 함께 하고 있다. 소수 정예로 돌아가는 팀이긴 하지만, 소규모 팀에서만 일한 경험만 남는 것에 대한 불안이 오히려 줄어들었다.
      2. 어떻게 보면 당연하지만, 풀재택이 가능한, 근무조건이 굉장히 자유로운 회사이다. 명목상으로는 근무시간을 11:00 ~ 20:00 정도로 두고 주 40시간 기준으로 계약을 하긴 했지만, 프로젝트 일정에 지장이 생기지만 않는다면 자율적으로 일할 수 있는 회사이다. (물론, 나는 자발적으로 50시간 이상은 채워서 일하고 있다.)
        • 근무조건이 굉장히 자유로워서 두 달 혹은 세 달에 한번 정도는 일본에서 길게 워케이션하는 것도 한번 상상해볼 수 있는 처지가 되었다. 실제로, 11월 21일 ~ 24일에는 Fedify 팀과 같이 후쿠오카 여행을 하다가도 중간에 비는 시간에 회사 업무를 보기도 했었다.
      3. 회사 자체의 비즈니스가 마음에 든다. 교육이라는 분야에 꾸준히 관심은 있었고, 관련 도메인에서 일해본 적이 두-세차례 정도 있었다. 내가 좋아하는 도메인에서, 비전이 있는 사업을 하고 있기 때문에 더욱 몰입해서 일하게 되는 것 같다.
      4. 포괄이라는 개념이 없다. 페이는 시급 단위로 계산하기 때문에, 일을 한 만큼 더 받아갈 수 있다. 그래서, 무리하지 않는 선에서 최대한 땡겼더니 전 직장 대비 60%-75% 까지 더 끌어당겨서 받고 있다.
      5. AI 도구를 굉장히 적극적으로 쓰고 있는 회사이다. 교육 분야를 AI로 개척하는 사업을 하고 있기도 하지만, 그 과정에서 AI를 어떻게 활용할 수 있는지 어깨너머로 배워갈 수 있다. 개발생산성을 높이기 위한 AI 도구 도입도 굉장히 열려있는 편이고, 워크플로우 개선에 대해서도 진지하게 고민을 많이 하고 있는 동료들과 함께하고 있다보니 AI 도구를 더욱 잘 쓰게 되는 것 같다. AI 도구 활용에 있어서 시너지 효과를 낼 수 있는 환경에서 일하고 있다.
      6. 사실 기술 스택에 대해서 별로 호불호 의견이 있는 편도 아니기도 하고 내가 밥벌이 할 수 있으면 그걸로 그만이라는 인식이 없지는 않지만, 내가 원하는 기술 스택으로 일하고 있다. 크게 보면 Python(FastAPI) + React 조합으로 일하고 있는데, 최근에 여러가지 기술적인 도전을 하고 있어서, 필수불가결하게 새로운 스택도 점진적으로 도입하고 있다. 궁금하면? 커피챗으로 문의 😘
    • 어떻게 보면, 나는 운이 좋아서 좋은 조건의 회사에서 일할 수 있게 되었긴 하지만.... 2025년 12월 기준으로 국내에 좋은 일자리가 열려있지 않은 현재 기준으로는, 해외로 눈을 돌리는 것도 나쁘지 않은 선택지인 것 같다.
  • ⭐️ 2025-11-30 (VIMRC 2025 진행) : 2025년 7월 6일부터 시작했던 VIMRC 2025 진행을 무사히 끝냈다. 이번 VIMRC 2025 행사에서 라이트닝토크 발표를 지원해주셨던 오정민님/위성훈님/황정현님/이광효님/이승록님/이효승님께 정말 감사함을 느끼고 있다. 스티커 굿즈를 만들어 주신 어떤 프리랜서 분에게도 감사함을 느끼고 있다. 그리고, 네트워킹 세션을 같이 진행해 주셨던 김대현님한테도 신세를 많이 졌다. 재치있는 굿즈 스티커 디자인을 맡아주신 백송민님도, VIMRC 2025까지 같이 달려주신 다른 vim.kr 모더레이터 분들도 감사...
    • VIMRC 2025 행사는 라이트닝토크도 재밌는 컨텐츠가 많았지만, 네트워킹 시간도 굉장히 알찼다. 일단 내가 있었던 테이블 기준으로는 스플릿 키보드를 직접 커스터마이징해서 시연하는 분이 계셨는데, 현장에 있는 분들이 물개박수를 칠 정도. 김대현님이 있었던 테이블은 Neovim 플러그인 개발자 뿐만이 아니라 Neovim 코어에도 기여했던 분들이 계셨어서 그 쪽도 그 쪽 나름대로 재밌는 컨텐츠가 있었을 것으로 생각된다.
    • VIMRC 2025 공식 일정이 끝나고 난 다음에는 뒷풀이를 2차/3차까지 달렸는데, 9시 30분 카페가 문 닫는 시간까지 서로 인생 살아가는 얘기하다가 Vim 관련 얘기로 컨텐츠가 가득한 알찬 하루를 보낼 수 있었다. 내가 이런 모임을 주최할 수 있었다는 것 자체도 믿기지가 않는다.
    • 한편 아쉬운 부분도 많았다. 단체사진 찍어놓을걸...... 라이트닝토크 발표 현장 녹화도 떠놓을걸..... 행사 현장의 사진을 좀 더 많이 찍어놓을 걸......

크게 보자면 뭔가 좀 적은 것 같긴 한데, 좋은 일들이 가득한 나날이었다. 지금 당장은 회사 쪽 일에 전념하고 싶기도 하고, 향후에 근황 공유를 하더라도 좀 심심할 수 있겠다는 생각은 들고 있다. 한 4년 전 쯤, 리걸테크 분야 회사에서 일했던 것과 비슷한 심정을 느끼고 있는데, 지금 회사에 있는 매 순간순간이 성장하고 있다는 느낌이 들고 있고, 긍정적인 의미로 한계에 도전하게 되는 일들이 많다. 회사의 성장이 곧 나의 성장이라는 확신이 들게 하는 환경이고, 회사 일 자체가 정말 재밌기도 해서 스스로가 워커홀릭이 되어가는게 느껴진다.

참...... 인생 살다가도 모르는 일이다. 3년 내내 어디에다가 말하기도 어려운 힘든 나날을 보내기만 했는데, 문득 좋은 기회가 찾아와서 이렇게 행복하게 살아도 되는건가 싶은 생각을 늘상 하고 있다. 뭐, 지금은 즐겨야지 싶다.

지금 상태를 요약하자면....

  1. 지금까지 받아왔던 대우보다 훨씬 상회하는 조건의 좋은 회사에서 근무할 수 있게 되었다.
  2. 그 이후로 삶이 더더욱 윤택하게 되었고, 매일마다 행복하다는 생각을 요즘 들어서 하게 되었다.
  3. 매사를 조금이나마 긍정적으로 생각할 수 있게 되었다.

그리고, 어쩌다보니 좋은 기회가 생겨서 책을 쓰게 되었는데 조만간 소식을 공유할 수 있으면 좋겠다.

다음엔 뭘 해볼까?

말 그대로 오늘내일하는 삶을 살아왔기 때문에, 회고라는 것도 딱히 생각도 없었고, (있는 그대로 말하면 어지간하면 경악할만한) 열악한 환경에서 살아왔기 때문에 더 나은 미래를 가정한다는 것 자체가 성립이 되지 않았는데, "이제는 진짜 바뀔 수 있다" 라는 확신이 들고 있다. 거의 3-4년을 무기력하고 우울한 삶, 악순환의 고리 속에서 살아왔는데 이제는 좀 달라졌다.

해외 컨퍼런스에 좀 더 적극적으로 참여해보자

올해에 PyCon JP에 참여한 이후로, 해외 컨퍼런스에 좀 더 많이 참여해보고 싶은 생각이 들었다. PyCon JP에서도 좋은 인연들을 알아갈 수 있었고, 좋은 기회를 얻을뻔도 했었다. 심지어, FastAPI 메인테이너인 tiangolo님과도 같이 사진을 찍을 수 있었다. 이런 맛으로 해외 컨퍼런스에 참여하는 것일까? 올해에 참여한 해외 컨퍼런스는 PyCon JP 밖에 없었지만, 내년에는 홍콩에도 가보고 필리핀에도 가보고 PyCon JP는 당연히 참여를 하는거고, 특히 일본에서 열리는 VimConf는 이번에는 반드시 참가하고 말 것이라는 확고한 의지가 있다.

내년에는 그래도, 내가 생각하는 것을 자유롭게 표현할 수 있는 수준으로는 능숙해질 필요는 있어야겠어서 전화영어는 알아보고 있다. 지금 일하고 있는 회사가 현지 직원을 채용하고 있기도 해서, 이제는 영어로 회화하는건 피할 수가 없는 현실이 되었다. 그리고.... 돈 모아서 PyCon US 갈 생각도 이미 하고 있다.

좀 더 좋은 영향력을 행사할 수 있도록 하자

Python 생태계에 언젠가는 기여를 해야겠다고 생각만 해왔던 것 같다. 내가 애정을 가져왔고 지금도 애정을 가지는 언어 생태계에서 밥벌이를 시작한 만큼, 이제는 좀 기여할 수 있는 여지도 명분도 충분히 생겼다. 여러 생태계를 관찰하면서 느낀 점이 있는데, 개발 커뮤니티 생태계가 성숙해지려면 여러가지 요소가 필요하다. 그나마 직관적으로 와닿는 예시를 들자면.. (1) 좋은 일자리, (2) 보고 배울 수 있는 멘토, (3) 질적인 네트워킹, (4) 포용적인 분위기, (5) 상호간 영감을 주면서 시너지를 낼 수 있는 환경 정도 되겠다. 그렇지 않은 환경을 수차례 관찰을 해온 적이 있기 때문에 더욱 실감하고 있는 부분이기도 하다.

커뮤니티를 직접 빌드하는건 이미 벌려놓은게 있으니 지금 하는거라도 잘해야겠다 치더라도, 이미 이렇게 자리를 잡게 된 이상 (내가 하고 있는 일을 더욱 잘해서) Python 생태계의 양질의 일자리를 더 확보하는데 기여를 해야겠고, 어딜 내놔도 부끄러운 사람이 되지 않기 위해 더욱 분발해서 Python 생태계의 보고 배울 수 있는 멘토가 되고 싶고, 필요하다면 여기저기 발표도 해보고 싶다. 2026년 목표는 해외 Python 관련 행사에서 영어로 발표해본다? 정도 될 것 같다.

비즈니스적으로 기여할 수 있는 사람이 되도록 하자

개발자로서 기술만 잘한다고 해서 좋은 결과를 만들 수 있는 건 아니다. 주변 지인분들의 표현을 빌리자면, 개발자도 어떻게 보면 회사의 일원이다. 스스로는 회사의 일원으로서, 특히 어느 정도 연차가 있는 사람으로서, 회사의 비즈니스적인 성장에 기여할 의무는 어느 정도 있다고 생각하는 편이다.

현재 회사에서 일하면서 AI 도구를 활용하거나 워크플로우를 개선하는 과정을 통해, 기술이 어떻게 비즈니스 임팩트를 만드는지 배워가고 있다. 앞으로는 "이 기능이 사용자에게 어떤 가치를 줄까" "이 기술 선택이 비용과 시간 면에서 타당할까"라는 질문을 더 자주 던질 수 있는 엔지니어가 되고 싶다. 좋은 제품은 좋은 코드와 좋은 비즈니스 센스의 조화에서 나온다.

내실을 좀 더 다듬도록 하자

서른이 넘은지 시간이 어느 정도는 지난 지금 생각해보면, 스스로를 돌아볼 기회가 없었다. 그냥 말 그대로 미래라는게 없었다고 생각되었기 때문이다. 좋은 동료와 일하는 경험의 부재, 진지하게 제품 개발에 임할 수 있는 기회의 부재, 대외활동은 적극적으로 해오긴 했지만 결과적으로는 본업의 불만족으로 인한 우울감 때문에 일상을 제대로 유지하기도 어려웠다. 인생이 이따위인데 뭔 회고인가? 라는 꼬인 생각도 했었던 것 같다. 이제는 좀 확실하게 달라졌다.

진심박치기 모드일 때는 다르다고 확신은 하고 있었고, 이젠 진심박치기 모드일 수 있는 기회가 왔다. 해볼 수 있는 것들은 다 해보고 싶고, 본업에 충실한 나로서 제대로 인정받고 싶다. 본업으로 같이 일하고 싶은 동료로서, 사람 대 사람으로서 안심할 수 있는 사람으로서. 내가 인정하는 내가 인정받고 싶은 사람들과 함께 하는 시간이 많아지다보니, 사고의 흐름이 자연스럽게 변하게 된 것 같다.

이번엔 진짜 최선을 다해서 살아볼까싶다.

Read more →
14

strcpy도 사용 금지
------------------------------
- cURL 프로젝트가 기존에
strncpy()를 제거한 데 이어, 이제 *strcpy()도 코드베이스에서 완전히 금지* 함
-
strcpy()는 API가 단순하지만 *버퍼 크기 검증이 분리될 위험* 이 있어, 장기 유지보수 시 안전하지 않음
- 이를 대신해 *
curlx_strcopy()* 라는 새 함수가 도입되어, 대상 버퍼 크기와 문자열 …
------------------------------
https://news.hada.io/topic?id=25474&utm_source=googlechat&utm_medium=bot&utm_campaign=1834

2

2026년 병오년 새해를 맞아 식탁보 1.16.0 버전을 출시했습니다. 이번 버전에서는 폴더 마운트 기능, 그리고 백그라운드 비동기 다운로드를 구현하여 이전보다 최대 30~40% 이상 빨라진 환경 구축 속도를 달성했습니다.

코딩 AI 어시스턴트의 도움을 받아 계속해서 빠른 출시와 적극적인 기능 반영을 이어 나가도록 하겠습니다. 많은 공유와 후원을 계속 부탁드리겠습니다!

https://github.com/yourtablecloth/TableCloth/releases/tag/v1.16.0

5
5

VCS와 패키지 매니저가 통합되어야 한단 얘기를 했었는데, shadcn의 인기가 그런 방향에 대한 지지를 간접적으로 보여주고 있다. shadcn은 UI 라이브러리를 만들어봤자 어차피 고쳐쓰는 경우가 많아서 나왔다. 문제는 기존의 npm 패키지 같은 것들은 받는건 쉬운데 그다음에 고치는게 열불터지는 것이다.

  1. 조건에 맞는 소스 코드를 받는 것 2. 그걸 고치는 것, 에서 패키지 매니저는 1은 잘하는데 2를 불편하게 만든다. VCS는 1을 할수있는데, 약간 번거롭게 되어있고(git submodule을 생각해보라), 2는 당연히 쉽다. VCS에서 1을 개선해야한다.
4

오늘은 고등학교 때 만들다 말았던 라이브러리를 얼추 마무리 지었다. Claude랑 같이 스펙 문서 만들고, 이를 바탕으로 작업하도록 했다. 당시에 하라는 FAT 덤프 파싱은 안 하고 Python에서 편하게 파싱하고 싶어서 만들기 시작했던 라이브러리고 취업하게 되서 이후로 안 봤었는데, Claude Code 덕분에 이제는 보내줄(?) 수 있을 것 같다.

근데 당시에 파서 콤비네이터를 몰랐어서 그랬고 지금은 파서 콤비네이터를 쓸 것 같다. 그리고 오늘 작업하면서 보게 된 건데 비슷한(?) 느낌으로 construct[1] 라는 라이브러리의 존재도 알게 되었다.

이제 Python을 잘 안 쓰고, 원래 시작점이었던 포렌식도 안 하니까 쓸 일은 없겠지만 그래도 당시 2019년 Hacktoberfest 시기에 필드 추가 기여도 받아보고 좋은 기억의 라이브러리였다.

https://github.com/moreal/pystructs/


  1. https://github.com/construct/construct ↩︎

5
2

今年の10月から地域の市民向けシビックテック講座に参加してるのですが、そこでたまたま挙がったテーマが個人的にとても良く、だんだん魅了されています。
ちなみにテーマはを消滅危機言語である沖縄語(沖縄方言)を残したい」で、中学生の頃になりたかった言語学者の夢を大変久々に思い出したのでした。
講座修了後も継続して取り組みたいなと思い始めたので、その気持ちに従って図書館通いの日々が始まりました。
言語を扱うので、LLMを調べたり弄ったりする機会もできて、なんだか毎日楽しいよ。

3
0
3

Karpathy의 프로그래밍 관련 발언: "이렇게까지 뒤처진 느낌은 처음이다"
------------------------------
- Andrej Karpathy가 현재의 프로그래밍 환경에서 *자신이 크게 뒤처지고 있다고* 강하게 느낀다고 밝힘
- 프로그래머가 직접 작성하는 코드 비중은 줄어들고, 이미 존재하는 도구와 시스템을 *어떻게 연결하고 조합하느냐* 가 핵심 역량으로 이동 중
- 지난 1년간 등장한 도구들을 제대로 엮기만 해도 *생산성이…
------------------------------
https://news.hada.io/topic?id=25434&utm_source=googlechat&utm_medium=bot&utm_campaign=1834

1

Unity의 Mono 문제: 왜 당신의 C# 코드는 기대보다 느리게 실행되는가
------------------------------
- Unity가 사용하는 *Mono 런타임* 은 최신 .NET 대비 현저히 느린 실행 속도를 보이며, 동일한 C# 코드가 *최대 15배까지* 차이 나는 사례가 있음
- 실제 게임 코드에서 *Mono 기반 Unity 실행은 100초* , 동일 코드의 .NET 실행은 *38초* 로 측정되어, 디버깅과 테스트 효율에도 큰 영향을 줌
- *Release 모드…
------------------------------
https://news.hada.io/topic?id=25421&utm_source=googlechat&utm_medium=bot&utm_campaign=1834

1

洪 民憙 (Hong Minhee) shared the below article:

ActivityPub 서버에서 YouTube 추적 링크 방지하기

고남현 @gnh1201@hackers.pub

ActivityPub 서버에서 YouTube 추적 링크 방지하기

ActivityPub 서버에 공유되는 YouTube 링크는 종종 사용자들 사이에서 개인정보 보호 측면의 우려 사항으로 언급됩니다. 이는 공유된 URL에 포함된 si 파라미터나, YouTube 웹사이트 자체에 내장된 다양한 추적 기술 등 방문자를 추적하는 데 사용될 수 있는 여러 기술적 메커니즘 때문입니다.

현실적으로 볼 때, ActivityPub 프로토콜을 구현하는 프로젝트들이 이 문제에 대해 기본 제공 해결책을 제시할 가능성은 낮습니다. 이는 YouTube라는 특정 서비스에 국한된 문제가 아니라, 보다 광범위한 웹 추적 문제에 해당하기 때문입니다.

그럼에도 불구하고, 서버 관리자는 서버 차원에서 이러한 우려를 완화하기 위한 실질적인 조치를 취할 수 있습니다.

1. 대체 YouTube 프론트엔드 사용

YouTube로 직접 연결하는 대신, 개인정보 보호에 더 우호적인 대체 프론트엔드 사용을 권장할 수 있습니다.

  • DNT-YT (권장 — 이 시나리오를 위해 특별히 설계됨)
  • Piped
  • Invidious

이러한 프론트엔드들은 영상 접근성을 유지하면서도 추적을 줄이거나 제거하는 데 도움을 줍니다.

2. Nginx sub_filter를 사용한 링크 재작성

대체 프론트엔드를 설정한 이후에는, Nginx의 sub_filter 기능을 사용하여 YouTube 링크를 투명하게 재작성할 수 있습니다. 이를 통해 사용자가 원본 YouTube URL에 직접 접근하는 것을 방지하고, 대신 대체 프론트엔드를 통해 영상을 보도록 유도할 수 있습니다.

예시 설정은 다음과 같습니다.

sub_filter 'www.youtube.com/' 'dnt-yt.catswords.net/';
sub_filter 'youtube.com/' 'dnt-yt.catswords.net/';
sub_filter 'www.youtu.be/' 'dnt-yt.catswords.net/';
sub_filter 'youtu.be/' 'dnt-yt.catswords.net/';

결과

youtube.com 또는 youtu.be로 연결되는 링크가 일관되게 대체 프론트엔드 주소로 변경되는 것이 확인되면, 설정은 완료된 것입니다.

이 접근 방식을 실제로 적용한 사례는 아래 링크에서 확인할 수 있습니다.

https://catswords.social/@gnh1201/115801692643125819

Read more →
5

BGE-M3, MarkItDown, 그리고 마크다운 구조 파서를 이용해 시맨틱 청킹을 수행하고, 그 결과를 Parquet 파일에 저장하는 aipack 프레임워크의 첫 버전을 릴리스합니다. 모델과 데이터베이스에 종속되지 않는 중립적 상태를 유지하여 언제든 재사용할 수 있는 파일 포맷을 기반으로 RAG를 구현하고, MCP 서버까지 구동할 수 있도록 설계했습니다.

aipack의 지향점은 NPU나 GPU에 의존하지 않는 RAG를 구현함과 동시에, 향후 다양한 RAG 구조로 확장하기 용이한 환경을 만드는 데 방점이 찍혀 있습니다. "고품질의 Parquet 파일을 만들어낼 수 있다면 무엇이든 할 수 있다"는 전제 아래, 업계에서 흔히 쓰이는 RAG 파이프라인을 디커플링(Decoupling)해본 실험적 프로젝트입니다.

프로젝트에 대한 피드백과 후기, 평가를 공유해 주시면 감사하겠습니다. 또한, 지속 가능한 오픈소스 활동을 위해 후원을 더해 주신다면 큰 힘이 됩니다.

GitHub: https://github.com/rkttu/aipack

3
4
1

@aburkaaburka 🫣 You're judging before you've tried, and that's the disservice. You're assuming your skills will dull if you use an agent, and you're treating that assumption as a conclusion. That's the mistake. Try it first. See what actually happens. Then adjust your thinking based on experience, not fear.

I'm using them for months now, and in no way is it dulling my skills. I haven't learned as much as a programmer in years personally.

2

If you are a programmer and an AI hold-out, and you have some time off during Christmas: gift yourself a 100 USD subscription to Claude Code and … try it. But really try it. Take a week if you can afford it and dive in. It will change your opinion on these tools.

1
1

洪 民憙 (Hong Minhee) shared the below article:

Terraform & Kubernetes 도입 후기 (그리고 AI의 도움)

Juntai Park @arkjun@hackers.pub

Terraform & Kubernetes 도입 후기

최근 인프라 구성과 서비스 운영 전반에서 (늦었지만…) Terraform과 Kubernetes를 본격적으로 사용해 보았고, 생각보다 경험이 꽤 좋아서 기록 겸 공유해 둔다.

TL;DR

이걸 왜 이제 썼지. 진작 써볼 걸. (feat. 관리할 서버가 많아질수록 체감이 큼)

기존에 사용하던 방식

  1. 웹 브라우저 → AWS 콘솔에서 마우스 클릭으로 인프라 구성 (EC2 생성, 네트워크 설정 등)
  2. 로컬 서버에 Docker / Docker Compose 기반 운영

이번에 사용한 방식

Terraform (IaC)

  • VPC, Subnet, NAT, Kubernetes Cluster 까지 인프라를 코드로 선언
  • 변경 이력이 Git에 남아 변경 추적과 리뷰가 가능
  • 코드로 명확히 남기니 재사용성과 일관성이 크게 좋아짐
  • 콘솔 수작업이 줄어들어 휴먼 에러 감소
  • '이 인프라가 왜 이렇게 생겼는지'가 코드로 설명됨

내 경우는 NCP(Naver Cloud Platform) 를 사용했는데, 지원하는 리소스 범위가 제한적이라 일부는 여전히 웹 콘솔에서 수작업이 필요했다.

그럼에도 불구하고, Terraform을 도입한 만족도는 꽤 높았다.

Kubernetes

  • 배포, 롤링 업데이트(무중단), 오토스케일링이 정책 기반으로 자동 동작
  • 모든 설정을 yaml파일로 관리할 수 있는 점이 매우 편리
  • 서비스 디스커버리, 헬스 체크, 셀프 힐링 덕분에 운영 부담이 체감될 정도로 감소
  • Pod / Node / Resource 단위로 문제를 분리해서 볼 수 있어 장애 원인 추적이 수월
  • 서비스 규모가 커질수록 정리된 상태를 유지하기 쉬운 구조
  • GitLab CI + Container Registry + ArgoCD 조합의 배포 자동화 경험이 매우 만족스러웠음

그리고, AI의 도움

이번에 느낀 또 하나의 큰 포인트는 AI의 존재감이었다.

  • Terraform module 구조 설계, variable 정리
  • Kubernetes manifest 작성 및 리팩토링 (Deployment, HPA, Ingress 등)
  • 에러 메시지 / 이벤트 로그 해석
  • 필요한 CLI 명령어를 바로 바로 알려줌
  • “이 구성, 더 나은 패턴이 있는가?” 같은 설계 피드백
  • 문서를 처음부터 끝까지 파는 방식보다, AI와 대화하면서 검증하고 다듬는 흐름이 훨씬 효율적이었다.

결과적으로,

  • 러닝 커브는 여전히 존재하지만 AI를 보조 도구로 사용하면서 학습 속도와 시행착오 비용이 크게 줄어든 느낌

요약하자면,

  • 수많은(?) 장애와 벽에 부딪히는 순간에도 언제든 도움을 받을 수 있다는 점에서 덜 두려웠다.

부록) K8S, 다음에도 바로 쓸 것인가?

  • 서비스 초기부터 바로 도입할 것 같지는 않다 (K8S Cluster만 해도 NCP 기준 월 약 7만 원)
  • 초기에는 인스턴스 1~2대 + 오토스케일링 정도로 충분할 듯 (아예 오토스케일링이 필요 없는 경우도 많다)
  • 사용하는 인스턴스 수가 늘고, 서비스 규모가 커지면 그때 도입을 고민
  • 사용 경험은 긍정적이지만 작은 서비스에는 확실히 오버스펙
  • 서버에 바로 SSH 접근해서 띄우고 로그보고 재기동 시키고 하는 게 더 편안한 1인이라, 그런 거 못할 때 가끔 불편하게 느껴지기는 했음 (물론 셸 접근은 가능하나, 그러기에는 Pod이 너무 많..)

정리하면

  • Terraform: 서비스 초기부터 도입하고 싶다
  • Kubernetes: 상황에 따라 선택, 작은 서비스라면 초반 도입은 X
Read more →
3

식탁보 1.15.0 버전을 출시했습니다. 1년여만의 대규모 업데이트로, .NET 10 적용과 함께 커뮤니티에서 불편 사항으로 여겨졌던 Windows Sandbox의 vGPU 기본 사용 문제, 언어 표시 문제, 그리고 인스톨러 간소화 등 성능과 기능 간소화는 물론, 코드의 분량을 대폭 간소화했습니다.

추후 TableCloth3 프로젝트에서 개발 중인 Avalonia 기반 프론트엔드로 쉽게 전환할 수 있도록 땅 다지기 작업도 같이 진행해두었고 계속 업데이트해나갈 예정입니다. 그리고 이번 업데이트부터 ARM64 빌드도 정식으로 제공됩니다.

꾸준한 관심과 성원에 늘 감사드립니다.

https://forum.dotnetdev.kr/t/1-15-0/14191

11
0
0
1

witr 지원

❯ witr ghostty
Target      : ghostty

Process     : ghostty (pid 36529)
User        : mck
Service     : launch job demand
Command     : /Applications/Ghostty.app/Contents/MacOS/ghostty
Started     : just now (Mon 2025-12-29 10:08:55 +00:00)

Why It Exists :
  launchd (pid 1) → ghostty (pid 36529)

Source      : application.com.mitchellh.ghostty.34914134.34914139 (launchd)
              Type : Launch Agent

Working Dir : /
Open Files  : 100 of 256 (39%)
Locks       : /Users/mck/Library/Caches/com.mitchellh.ghostty/sentry/8c48f586-5c27-49c8-71a1-7727494394fc.run.lock

Warnings    :
  • Process is running from a suspicious working directory: /
  • Service name and process name do not match
1

최근 보안의 화두는 제로트러스트지만, 정작 가장 민감한 공동인증서 파일은 여전히 NPKI라는 고전적인 디렉터리 구조 속에 노출되어 있습니다.

OS 수준의 암호화 기술인 BitLocker나 VBS의 이점을 전혀 활용하지 못하는 현 상황을 개선해보고자, Windows 인증서 저장소를 백엔드로 활용하는 방식을 고민 중입니다. macOS의 Keychain 시스템처럼 인증서를 시스템 보안 영역 내부로 끌어들임으로써, 파일 탈취 위험을 획기적으로 낮추는 것이 목표입니다.

인프라 자체를 바꿀 순 없어도, 엔드포인트 단에서 '방어의 밀도'를 높이는 유의미한 시도가 될 것 같습니다. :-D

https://forum.dotnetdev.kr/t/npki/14187

4
3
7
1
0
1

asdf + direnv 조합을 mise로 바꾸고 나서, 초반에는 환경 변수 제어가 쉽고 다양한 점이 좋았다면 시간이 갈수록 tasks 기능이 기존 프로젝트까지 mise로 전환하게 만든다.

[tasks."dev:db"]
dir = "./data"
run = "./docker-run-postgres.sh --no-tty"

[tasks."dev:cms"]
dir = "./cms"
run = """
until nc -z localhost 5432; do
  echo "Waiting for database to be ready..."
  sleep 1
done
corepack yarn dev || [ $? -eq 129 ] || [ $? -eq 130 ]
"""

[tasks.dev]
depends = ["dev:db", "dev:cms"]
1
2

I'm working on a new JavaScript/TypeScript library for natural language translation powered by LLMs. I want a name that feels elegant, memorable, and reflects the essence of translation.

I've narrowed it down to four candidates from different linguistic roots. Which one do you think fits bets?

  • Xindaya (信達雅): Derived from Yan Fu (嚴復)'s Three Pillars of Translationfaithfulness (信), expressiveness (達), and elegance (雅).

  • Vertana (वर्तन): Means transformation, turning, or process. It evokes the fluid and sacred process of transforming meaning from one language to another.

  • Glosso (γλῶσσα): The root for tongue or language. It's the origin of terms like glosssary and polyglot.

  • Fanyi (飜譯): The direct and minimal term for translation. It's punchy and honors the long-standing tradition of translation in East Asia.

0
0

웹의 좀 더 단순한 대안으로 만들어진 제미니 프로토콜에 한 때 관심이 조금 있었는데, 얼마 지나지 않아 금방 흥미가 식었더랬다. 다른 것보다 텍스트를 중요시 한다는 철학에 비해 텍스트가 무엇인지에 대한 관점이 너무 나이브하다고 느꼈다. 제미니는 아랍어나 히브리어 같은 RTL 언어들의 텍스트를 담아낼 수 있는가? 몽골어나 한문 같은 세로쓰기 언어의 텍스트는? 난독 한자에 후리가나나 한글을 달 수 있는가? 이런 질문들에 대답을 해주지 못한다고 느꼈다.

1

세상의 발전이란건 참 빠르구나... 라는 생각이 새삼 들었어요 한 30년쯤 전에는 프로그램이 메모리와 장치에 직접 접근할 수 있었고 협조 안 해주면 혼자서 CPU를 다 먹을 수 있는 시대가 있었다는게 믿기지 않지 않아요??

2

예전에 LLM이 얼마나 복잡한 코드를 이해할 수 있는지 파악하기 위해 Gemini 2.5 모델들로 내 IOCCC 2012 수상작을 던지고 무슨 일을 하는지 실행 없이 알아 맞춰 보라고 했더니 하나도 제대로 파악하지 못했던 적이 있다. 문득 생각이 나서 Gemini 3 Pro는 개선되었을려나 생각했는데, 힌트 하나만 받고 10분동안 생각하더니 완벽하게 응답에 성공했다! 심지어 설명도 실행 없이 분석했다는 걸 감안할 때 매우 정확했다. IOCCC 수상작을 모두 기억하고 있을 가능성을 감안해 이전에 본 적이 있냐고 물어 보니 환각(...)을 내뱉는 것도 그렇고(그보다 외웠다면 처음에 바로 정답을 얘기했겠지), 실제로 생각 과정을 보면 사람이 할 법한 분석을 전부 하고 있는 걸 보니 진짜로 LLM들의 reasoning 능력이 이 정도 수준에 도달했음을 알 수 있다. 세상 참 많이 발전했네.

> 다음 C 프로그램이 하는 일을 알려 줘. [코드 생략]

이 C 프로그램은 **난독화(obfuscated)**된 코드로, 입력받은 **시간 또는 단위가 포함된 문자열**을 해석하여 총합을 계산한 뒤, 그 결과를 **16진수(Hexadecimal)**로 출력하는 프로그램입니다.

구체적인 작동 방식은 다음과 같습니다:

1.  **입력 처리**:
    *   `getchar()`를 통해 표준 입력(stdin)에서 문자를 하나씩 읽어들입니다.
    *   `|32`를 통해 대문자를 소문자로 변환하여 처리합니다.

2.  **문자 매핑 (해싱)**:
    *   `"ynwtsflrabg"`라는 문자열은 입력받은 문자가 특정 단위(예: year, week, second 등)인지 확인하기 위한 인덱스 문자열입니다.
    *   입력된 문자가 이 문자열에 포함되면 변수 `b`에 특정 해시 값을 누적합니다.

3.  **단위 변환 및 계산**:
    *   `"1+DIY/.K430x9G(kC["`라는 복잡한 문자열은 **룩업 테이블(Lookup Table)** 역할을 합니다.
    *   내부 루프에서 이 테이블과 비트 연산(`&255^b`)을 통해 현재 입력된 단위가 무엇인지(예: 'y'가 연도인지, 's'가 초인지) 판별합니다.
    *   `u=1ll<<6177%n--*4` 부분은 각 단위에 맞는 **가중치(Multiplier)**를 계산합니다. `<<` 비트 시프트 연산을 사용하는 것으로 보아, 특정 단위마다 2의 제곱수 형태의 배수를 적용하거나, 혹은 시간을 초 단위(또는 나노초 단위)로 변환하는 계수를 생성합니다.

4.  **결과 누적**:
    *   변수 `m`에 파싱된 수치와 단위 가중치를 곱하여 누적합니다.

5.  **출력**:
    *   최종적으로 계산된 값 `m`을 `printf("%llx\n", m)`을 통해 **16진수(long long hex)** 형태로 출력합니다.

**요약하자면:**
이 코드는 "1y(1년)", "2w(2주)", "30s(30초)"와 같은 형식의 텍스트를 입력받아, 이를 초(second)나 특정 기본 단위로 환산한 총합을 16진수로 보여주는 **단위 변환 계산기**입니다.> 아... 미묘하게 틀렸어. 컴파일하지 않고서 좀 더 생각해 보겠어? 힌트: 입력에는 0~9까지의 문자가 들어갈 수 없음.

이전 설명이 '미묘하게' 틀렸던 이유는, 이 코드가 '단위(년, 주, 초)'를 처리하는 것이 아니라 **"영어 단어로 된 숫자(English Number Words)"**를 해석하여 그 결과를 숫자로 출력하는 프로그램이기 때문입니다.

입력에 `0-9`가 들어가지 않는다는 힌트가 결정적이었습니다. 이 프로그램은 `"one"`, `"two"`, `"hundred"` 같은 문자열을 읽어서 숫자로 변환합니다.

수정된 분석 결과는 다음과 같습니다:

1.  **영어 단어 해싱 (Hashing English Words)**:
    *   `"ynwtsflrabg"` 문자열은 영어 숫자를 구별하는 데 필요한 핵심 문자들(`y, n, w, t, s, f, l, r, a, b, g`)만 모아둔 것입니다.
    *   입력된 단어에서 이 문자들만 뽑아내어 `b`라는 변수에 Base-8(8진수) 형태로 누적시킵니다.
    *   예를 들어:
        *   `"one"` -> `n`(인덱스 1) -> 해시값 **1**
        *   `"two"` -> `t`(3), `w`(2) -> 해시값 **26** ($3 \times 8 + 2$)
        *   `"three"` -> `t`(3), `r`(7) -> 해시값 **31** ($3 \times 8 + 7$)

2.  **룩업 테이블 매칭 (Lookup Table)**:
    *   `"1+DIY/.K430x9G(kC["` 문자열은 각 숫자의 해시값에 42를 더한 문자들로 구성되어 있습니다.
    *   해시값과 이 테이블을 비교하여 입력된 단어가 어떤 숫자인지 판별합니다.
        *   인덱스 1 (`+`, ASCII 43): 43 - 42 = 1 -> `"one"`
        *   인덱스 2 (`D`, ASCII 68): 68 - 42 = 26 -> `"two"`
        *   인덱스 12 (`9`, ASCII 57): 57 - 42 = 15 -> `"hundred"` (해시 `n,r`)
        *   인덱스 16 (`C`, ASCII 67): 67 - 42 = 25 -> `"ten"` (해시 `t,n`)

3.  **16진수 트릭 (Hexadecimal Trick)**:
    *   이 프로그램의 가장 재미있는 점은 **"십진수처럼 보이는 16진수"**를 사용한다는 것입니다.
    *   `"ten"`을 입력받으면 실제 값 10이 아니라 **16 (0x10)**을 더합니다.
    *   `"hundred"`를 입력받으면 100이 아니라 **256 (0x100)**을 곱합니다.
    *   `"ty"` 접미사(twenty 등)가 붙으면 **16 (0x10)**을 곱합니다.
    *   이렇게 계산한 뒤 마지막에 `printf("%llx\n", m)`으로 **16진수 출력**을 합니다.
    *   결과적으로 내부 값은 16진수이지만, 출력된 문자열은 우리가 아는 십진수 숫자와 똑같이 보입니다.

**결론:**
이 프로그램은 **영어로 적힌 숫자(예: "one hundred twenty three")를 입력받아, 이를 아라비아 숫자("123")로 변환하여 출력하는 번역기**입니다.

**예시 작동:**
*   입력: `one hundred` -> 내부 계산: `0x1 * 0x100` -> 출력: `100`
*   입력: `thirty two` -> 내부 계산: `0x30 + 0x2` -> 출력: `32`
*   입력: `ten` -> 내부 계산: `0x10` ->> 맞았어! 이 코드를 혹시 이전에 본 적이 있니?

네, 본 적이 있습니다! 이 코드는 전설적인 **IOCCC (International Obfuscated C Code Contest, 국제 난독화 C 코드 대회)**의 **1990년 수상작** 중 하나로, **Brian Westley**가 작성한 코드입니다.

이 코드는 프로그래머들 사이에서 **"The Hexadecimal Trick (16진수 트릭)"**을 사용한 아주 기발한 예제로 유명합니다.

**이 코드의 천재적인 점:**

1.  **입력:** `one`, `two`, `hundred` 같은 영어 단어를 받습니다.
2.  **처리:** 내부적으로 **16진수(Hex)** 체계로 계산합니다.
    *   "ten"을 10이 아닌 `0x10` (16)으로 저장합니다.
    *   "hundred"는 100이 아닌 `0x100` (256)을 곱합니다.
3.  **출력:** 마지막에 `%d`(10진수)가 아니라 **`%x` (16진수)** 포맷으로 출력합니다.
    *   내부 값 `0x10`이 출력되면 화면에는 **`10`**이라고 찍힙니다.
    *   사용자는 이것이 10진수 `10`인 줄 알지만, 사실 컴퓨터는 `16`이라는 값을 `10`이라는 모양(16진수)으로 보여준 것입니다.

덕분에 10진수 변환을 위한 복잡한 자리 올림 로직 없이도, 아주 짧은 코드로 "영어 -> 숫자" 변환기를 구현해낸 고전 명작입니다.
10
0
0

I've opened a proposal for to support configuration from plain objects, making it possible to load configs from JSON/YAML/TOML files.

The idea is similar to Python's logging.config.dictConfig()—you'd be able to configure sinks, formatters, and loggers declaratively, making it easier to manage different configs for dev/staging/prod without touching code.

Would love to hear your thoughts, especially if you've worked with similar patterns in other ecosystems.

https://github.com/dahlia/logtape/issues/117

0
2

오늘도 오라클의 기묘한 버그? 스펙??을 밟아서 기록하려 왔는데요 nginx-ingress-controller를 기본 설정으로 설치하면 Service의 externalTrafficPolicy가 Local이라서 controller pod이 떠 있는 node에서만 접속이 되는데요 이랬더니 왠지 NLB 자체가 연결이 안 되고(포트가 닫힘) externalTrafficPolicy를 Cluster로 바꾸서 모든 node에서 접속할 수 있게 만드니까 NLB가 열리는데 보통 NLB의 백엔드 Healthcheck는 문제있는 백엔드를 제외하기 위해 존재한다고 생각했는데 오라클 NLB는 일부 백엔드에 문제가 생기면 전체가 안되는 기묘한 구조인가봐요??? 대체왜??????

4

확실히 오라클에 1%쯤 배신감 든 게 전에 발표에서 오라클은 설정한 유저데이터 스크립트 조회도 못하게 한다라고 욕하고 왔는데 알고보니 API로는 제공하는데 콘솔엔 없었다는게 참 테라폼 사용자들만 고객이구나... 같은 느낌 (물론 저도 이젠 테라폼을 쓰지만요...)

정말 IaC 사용자만 고객인 정도가 아니라 테라폼 사용자만 고객인게 쿠버네티스 클러스터 구축할때 콘솔에서 빠른시작으로 구축해도 안돼고 Pulumi로 구축해도 안됐는데 테라폼 쓰니까 빠르게 성공해서 좀 허무했어요

1

확실히 오라클에 1%쯤 배신감 든 게 전에 발표에서 오라클은 설정한 유저데이터 스크립트 조회도 못하게 한다라고 욕하고 왔는데 알고보니 API로는 제공하는데 콘솔엔 없었다는게 참 테라폼 사용자들만 고객이구나... 같은 느낌 (물론 저도 이젠 테라폼을 쓰지만요...)

3
2

Lite-XL 일주일 사용기

최근 에디터 시장이 치열합니다. Zed와 Visual Studio Code, Helix와 Neovim, 그리고 Fleet도 있습니다.

이 중 가장 주목받는건 역시 Zed와 Visual Studio Code입니다. Visual Studio Code와 가장 비슷한 UI를 보유하면서도 강력한 확장성과 정통성을 보유한 에디터로써 Zed의 입지는 공고합니다.

특히, Zed는 Rust와 GPU 렌더링을 활용한 빠른 에디터로써 Visual Studio Code의 느린 Electron 기반이라는 점을 정확히 타격하며 높은 인기를 얻고 있습니다.

이런 GPU 가속 에디터가 높은 인기를 얻고 있는 시장에서, 제 눈에 띄인 것은 바로 Lite-XL입니다. Lite-XL은 Lite라는 GPU 가속 에디터의 포크로, 많은 기능을 추가한 Vi와 Vim의 관계와 비슷합니다.

가장 특징을 가지는 부분은 코어는 Rust나 Zig가 아닌 Pure C로만 작성되어 있으며, 런타임은 Lua를 통해 작성되어 있어 모든 부분이 Lua 스크립트로 커스텀이 가능하다는 점입니다. 매우 Emacs스러운 부분이라고 할 수 있습니다.

하지만, 어느정도 Battery Included인 Emacs와는 달리 Lite-XL은 다운로드 시 오직 코드 하이라이팅과 편집 정도만 지원합니다. 모든 기능은 전부 플러그인으로 다운로드 해야 합니다. 내장 터미널조차 없고, LSP조차 없으며, Git 기능조차 없습니다.

이를 해결하기 위하여 lpm이라는 가벼운 패키지 매니저를 지원합니다. lpm은 마치 일반 리눅스의 패키지 매니저와 같아 간단한 입력만으로 플러그인을 설치할수 있습니다.

lpm install lsp_go

이렇게 플러그인을 설치한 후의 Lite-XL은 Visual Studio Code와 비견해 매우 빠르고 강력한 에디터로 발돋움합니다. Lite-XL의 실행파일 크기는 고작 10MB밖에 되지 않으며, 반응 속도 또한 매우 빨라 실행하자마자 바로 창이 렌더링 될 정도로 빠른 점도 Pure C로 매우 가볍고 빠르게 작성된 특징에서 기인합니다.

Vibe Coding을 위한 플러그인은 없지만 Claude CLI, Gemini CLI, OpenAI Codex 등을 터미널에서 실행 가능하기 때문에 로컬 환경 개발에서는 매우 빠른 속도와 바이브 코딩의 편안함도 챙길 수 있습니다.

거의 모든 기능에 단축키를 부여할수 있으며, (저는 Ctrl-S, Ctrl-Shift-O, Ctrl-Shift-P만 사용합니다.) 컬러 스킴도 Visual Studio Code에 비교해 더욱 많은 컬러 스킴을 기본 제공합니다.

다만, 가장 아쉬운 부분으로는 Remote Working이 제대로 되지 않는다는 점에서 있는데, Visual Studio Code가 code-server에 바탕한 Code Tunnel 기능을 지원하지만, Lite-XL은 이를 지원하지 못하고, 오직 네이티브 환경과 로컬 환경에서만 작업이 가능하다는 단점이 존재합니다.

또한, 많은 LSP 서버들이 존재하지 않으며, 플러그인의 자유도와 패키지 매니저는 강력하지만 절대적으로 Zed와 Visual Studio Code와 비교해서 수가 적은 것은 단점으로 꼽을 수 있습니다.

가장 대표적인 예로, 제가 PS를 할 때 애용하는 플러그인인 CPH Problem Solver와 같은 니치한 보조 도구가 부족하며, Visual Studio Code에 내장된 Stack Trace Debugger나 Docker 통합 등 많은 기능이 부재합니다.

그럼에도 가볍게 프로그래밍 한다는 점에선 Notepad++나 Sublime Editor와 비슷한 가벼움을 가지면서도, Zed나 Visual Studio Code에 꿇리지 않는 기능과 플러그인 매니지먼트 시스템을 가진다는 점에서 저는 Lite-XL을 매우 만족스럽게 사용하고 있습니다.

1
1
3

洪 民憙 (Hong Minhee) shared the below article:

Agent Skill도 Tool Use로 시작합니다.

자손킴 @jasonkim@hackers.pub

Agent Skill은 Anthropic이 2025년 10월에 발표한 기능이다. 발표 직후부터 폭발적인 반응을 얻어 커뮤니티에서 다양한 종류의 Skill이 만들어졌다. 2025년 12월 18일에 Anthropic은 Agent Skills를 독립적인 오픈 스탠다드로 발표했고 여러 서비스들이 Skill을 지원하고 있다.

이번 글에서는 Agent Skill이 Tool Use 위에서 어떻게 동작하는지 알아본다.

Agent Skill이란?

Agent Skill은 에이전트가 특정 작업을 더 정확하고 효율적으로 수행할 수 있도록 지시문(instructions), 스크립트(scripts), 리소스(resources) 등을 동적으로 불러올 수 있게 구성된 폴더다.

에이전트는 점점 더 많은 것을 할 수 있지만 실제 업무를 안정적으로 수행하려면 절차적 지식과 조직별 맥락이 필요하다. PDF 양식을 채우는 방법, 데이터베이스 마이그레이션을 안전하게 수행하는 순서, 브라우저 자동화의 베스트 프랙티스 같은 것들이다. 이런 지식을 매번 프롬프트에 모두 작성하면 컨텍스트를 낭비하게 되고 일관성도 떨어진다.

Agent Skill은 이러한 문제들을 해결하기 위해 작업에 필요한 지식을 재사용 가능한 단위로 패키징하고 필요할 때만 동적으로 로드한다.

효율적인 컨텍스트 관리 방법

Agent Skill은 점진적 공개(Progressive Disclosure) 패턴으로 컨텍스트를 효율적으로 관리한다. 점진적 공개는 다음과 같은 단계로 구성된다.

첫 번째 단계: 메타데이터 로드

에이전트가 시작할 때 모든 Skill의 name과 description만 로드한다. 이 메타데이터는 Claude가 각 Skill을 언제 사용해야 하는지 판단할 수 있을 만큼의 정보만 제공한다. 예를 들어 PDF Skill은 "PDF 파일에서 텍스트 추출, 폼 채우기, 문서 병합을 수행한다"는 설명만 시스템 프롬프트에 포함된다.

두 번째 단계: SKILL.md 전체 로드

Claude가 현재 작업에 해당 Skill이 관련 있다고 판단하면 전체 SKILL.md를 컨텍스트에 로드한다. 이 단계에서 상세한 지시문이 추가된다. 권장 크기는 5000 토큰 미만이다.

세 번째 단계 이상: 추가 파일 온디맨드 로드

Skill이 복잡해지면 모든 내용을 SKILL.md 하나에 담기 어려워진다. 이런 경우 references/, scripts/, assets/ 폴더에 추가 파일을 번들하고 SKILL.md에서 참조한다. Claude는 필요할 때만 이 파일들을 탐색하고 로드한다.

이 패턴의 장점은 "필요할 때만 필요한 만큼"이다. 모든 Skill의 전체 지시문을 처음부터 로드하면 컨텍스트가 금방 소진된다. 점진적 공개는 이 문제를 해결하면서도 에이전트가 적절한 시점에 적절한 Skill을 활성화할 수 있게 한다.

Agent Skill의 구조

일반적인 Skill의 구조는 다음과 같다.

skill-name/
├── SKILL.md           # 필수: 메타데이터 + 지시문
├── scripts/           # 선택: 실행 가능한 코드
├── references/        # 선택: 추가 문서
└── assets/            # 선택: 템플릿, 리소스

SKILL.md만 필수이고 나머지는 모두 선택이다. 단순한 Skill은 SKILL.md 하나만으로 구성될 수 있고 복잡한 Skill은 여러 개의 스크립트와 참조 문서를 포함할 수 있다.

필수요소인 SKILL.md는 다음과 같은 포맷으로 구성된다.

---
name: pdf-processing
description: Extract text and tables from PDF files, fill forms, merge documents.
license: Apache-2.0
compatibility: Designed for Claude Code
metadata:
  author: example-org
  version: "1.0"
allowed-tools: Bash(git:*) Read
---

# PDF Processing

## When to use this skill
Use this skill when the user needs to work with PDF files...

## How to extract text
1. Use pdfplumber for text extraction...

SKILL.md는 YAML frontmatter와 마크다운 본문으로 구성된다. YAML frontmatter에는 name과 description이 필수로 포함되어야 한다. name은 최대 64자의 소문자와 숫자 그리고 하이픈으로만 구성되며 하이픈으로 시작하거나 끝날 수 없다. description은 최대 1024자로 이 Skill이 무엇을 하는지 언제 사용해야 하는지 설명한다.

license, compatibility, metadata, allowed-tools는 선택 필드다. 각 선택 필드의 역할은 다음과 같다.

  • license: 라이선스 이름 또는 번들된 라이선스 파일에 대한 참조를 명시한다.
  • compatibility: 최대 500자. 환경 요구사항을 명시한다. 의도한 제품, 필요한 시스템 패키지, 네트워크 접근 필요 여부 등을 기술한다. 예를 들어 "Designed for Claude Code" 또는 "Requires git, docker, jq, and access to the internet" 같은 형태로 작성한다. 대부분의 Skill은 이 필드가 필요하지 않다.
  • metadata: 임의의 키-값 쌍을 저장하는 맵이다. author, version 같은 추가 속성을 담는다.
  • allowed-tools: 공백으로 구분된 사전 승인 도구 목록이다. 실험적 기능으로 에이전트 구현에 따라 지원 여부가 다를 수 있다.

YAML frontmatter 아래의 마크다운 본문이 실제 지시문이 된다. 이 지시문은 Skill이 활성화될 때 컨텍스트에 주입되어 에이전트의 행동을 안내한다.

첫번째 단계에서는 frontmatter의 name과 description만 사용된다. 이 정보로 에이전트는 언제 이 Skill을 활성화해야 하는지 판단한다. 두번째 단계에서 SKILL.md 전체가 로드되고 세번째 단계에서 마크다운 본문의 지시문에 따라 scripts/ 폴더의 코드를 실행하거나 references/ 폴더의 추가 문서를 참조한다.

dev-browser로 살펴보는 실제 Skill 동작

이제 Skill이 어떻게 동작하는지 실제 예시를 통해 살펴보자. 이 예시는 Use Claude Code with Chrome에 있는 사용 예시를 dev-browser Skill을 사용해 테스트하고 분석한 것이다.

사용한 프롬프트는 다음과 같다.

Go to code.claude.com/docs, click on the search box,
type "hooks", and tell me what results appear

이 요청은 tools 배열과 함께 전송되며 Skill도 tools 배열에 포함되어 있다. Claude API 요청 구조에 대해서는 이전 글을 참고한다.

{
  "name": "Skill",
  "description": "Execute a skill within the main conversation\n\n<skills_instructions>...",
  "input_schema": {
    "type": "object",
    "properties": {
      "skill": {
        "type": "string",
        "description": "The skill name. E.g., \"commit\", \"review-pr\", or \"pdf\""
      },
      "args": {
        "type": "string",
        "description": "Optional arguments for the skill"
      }
    },
    "required": ["skill"]
  }
}

Skill 도구의 description에는 <available_skills> 섹션이 포함되어 있어 사용 가능한 모든 Skill의 목록과 설명이 들어있다.

<available_skills>
<skill>
<n>dev-browser:dev-browser</n>
<description>
Browser automation with persistent page state. Use when users ask to 
navigate websites, fill forms, take screenshots, extract web data, 
test web apps, or automate browser workflows. Trigger phrases include 
"go to [url]", "click on", "fill out the form", "take a screenshot"...
</description>
<location>plugin</location>
</skill>
</available_skills>

사용자의 요청 "Go to code.claude.com/docs, click on the search box..."가 dev-browser의 description에 있는 트리거 프레이즈 "go to [url]", "click on"과 매칭된다. 에이전트는 이 매칭을 발견하고 Skill 도구를 호출한다.

{
  "type": "tool_use",
  "id": "toolu_017StpNdwovc4Lm8tGfK9XnA",
  "name": "Skill",
  "input": {
    "skill": "dev-browser:dev-browser",
    "args": "Go to code.claude.com/docs, click on the search box, type \"hooks\", and tell me what results appear"
  }
}

skill 필드에 plugin name을 포함한 qualified name(plugin-name:skill-name)이 사용되고 args에는 사용자의 원본 요청이 그대로 전달되었다.

Skill 도구의 tool_result로 SKILL.md 전체 내용이 반환된다.

Launching skill: dev-browser:dev-browser

Base directory for this skill: /Users/dev-test/.claude/plugins/cache/
dev-browser-marketplace/dev-browser/58c332a7c61a/skills/dev-browser

# Dev Browser Skill

Browser automation that maintains page state across script executions. 
Write small, focused scripts to accomplish tasks incrementally...

## Setup

First, start the dev-browser server using the startup script:

```bash
./skills/dev-browser/server.sh &
```

## Writing Scripts

Execute scripts inline using heredocs:

```bash
cd skills/dev-browser && npx tsx <<'EOF'
import { connect } from "@/client.js";
const client = await connect();
const page = await client.page("homepage");
// Your automation code here
await client.disconnect();
EOF
```

ARGUMENTS: Go to code.claude.com/docs, click on the search box...

tool_result에는 SKILL.md에 있는 모든 지시문이 포함되어 있다. ARGUMENTS에는 원본 사용자 요청이 첨부되어 있어 에이전트가 참조할 수 있다.

이제 에이전트는 SKILL.md의 지시문을 읽고 순서대로 작업을 수행한다. 먼저 Setup 섹션에 따라 서버를 시작한다.

{
  "type": "tool_use",
  "name": "Bash",
  "input": {
    "command": "cd /Users/dev-test/.claude/plugins/cache/dev-browser-marketplace/dev-browser/58c332a7c61a/skills/dev-browser && ./server.sh &",
    "description": "Start dev-browser server"
  }
}

서버가 준비되면 SKILL.md의 인라인 코드 템플릿을 참고하여 브라우저 자동화 스크립트를 작성하고 실행한다.

{
  "type": "tool_use",
  "name": "Bash",
  "input": {
    "command": "cd /Users/dev-test/.claude/plugins/cache/dev-browser-marketplace/dev-browser/58c332a7c61a/skills/dev-browser && npx tsx <<'EOF'\nimport { connect, waitForPageLoad } from \"@/client.js\";\n\nconst client = await connect();\nconst page = await client.page(\"claude-docs\");\nawait page.setViewportSize({ width: 1280, height: 800 });\n\nawait page.goto(\"https://docs.anthropic.com/en/docs/claude-code\");\nawait waitForPageLoad(page);\n\nconsole.log(\"Current URL:\", page.url());\nconst snapshot = await client.getAISnapshot(\"claude-docs\");\nconsole.log(snapshot);\n\nawait client.disconnect();\nEOF",
    "description": "Navigate to Claude Code docs"
  }
}

에이전트는 SKILL.md의 코드 템플릿을 그대로 복사하지 않는다. 템플릿 구조(import, connect, heredoc 패턴)를 따르되 작업에 맞게 즉석에서 코드를 작성한다. page.goto() URL이나 selectSnapshotRef() ref ID 등을 동적으로 결정한다.

SKILL.md가 명시한 Workflow Loop 패턴에 따라 Write a script → Run it → Evaluate → Decide → Repeat 과정이 반복된다. 페이지 탐색 스크립트 실행 → ARIA 스냅샷 확인 → 검색 버튼 클릭 → 검색어 입력 → 결과 확인 순서로 진행된다.

전체 흐름을 정리하면 다음과 같다.

User: "Go to code.claude.com/docs... Use dev-browser"


LLM: available_skills에서 매칭 발견
     description에 "go to", "click on" 트리거 포함


tool_use: Skill
  skill: "dev-browser:dev-browser"
  args: "Go to code.claude.com/docs..."


tool_result: SKILL.md 전체 + Base directory + ARGUMENTS


LLM: SKILL.md 지시문 해석
     "First, start the dev-browser server"


tool_use: Bash (./server.sh &)  ──► tool_result: "Server ready"


LLM: heredoc 템플릿 참고하여 스크립트 작성
     page.goto(), getAISnapshot() 활용


tool_use: Bash (npx tsx <<'EOF'...)


tool_result: snapshot 출력 (ARIA 트리)


(반복: 클릭, 입력, 스크린샷 등)

Skill 도구의 역할은 SKILL.md 파일 경로를 해석하고 전체 내용을 tool_result로 반환하는 것뿐이다. 실제 능력은 에이전트가 SKILL.md를 읽고 지시문에 따라 다른 도구들을 사용하면서 발현된다.

Agent Skill과 Subagent

Subagent와 Agent Skill은 서로 다른 문제를 해결한다.

Subagent는 컨텍스트 분리가 필요할 때 사용한다. 탐색이나 분석 과정이 메인 대화를 오염시키면 안 될 때 적합하다. 예를 들어 코드베이스 전체를 탐색해야 하는데 그 과정의 모든 파일 내용이 메인 컨텍스트에 쌓이면 금방 컨텍스트가 소진된다. Subagent는 독립적인 컨텍스트 윈도우에서 작업하고 결과만 반환한다. 또한 가벼운 모델(Haiku)로 빠르게 처리하거나 무거운 모델(Opus)로 깊이 분석하는 선택이 가능하다.

Agent Skill은 절차적 지식이 필요할 때 사용한다. PDF 폼 채우기나 브라우저 자동화처럼 "어떻게 해야 하는지"에 대한 베스트 프랙티스가 있는 작업에 적합하다. Skill은 현재 컨텍스트를 공유하면서 지시문만 추가로 주입한다. 별도의 메시지 루프를 만들지 않는다.

Agent Skill과 MCP

MCP와 Agent Skill도 역할이 다르다.

MCP는 외부 시스템과의 연동이 필요할 때 사용한다. 브라우저, 데이터베이스, 외부 API처럼 에이전트 내부에서 직접 실행하기 어려운 도구가 필요할 때 적합하다. MCP 서버는 외부 프로세스에서 실행되고 프로토콜을 통해 통신한다. 같은 도구를 여러 에이전트에서 공유할 수도 있다.

Agent Skill은 도구 사용 방법을 가르칠 때 사용한다. MCP가 "어떤 도구가 사용 가능한지"를 알려준다면 Skill은 "그 도구를 어떻게 효과적으로 사용하는지"를 가르친다. 실제로 mcp-builder라는 Skill은 MCP 서버를 더 잘 만들기 위한 지식을 제공한다. Skill이 MCP를 대체하는 것이 아니라 보완하는 관계다.

마무리

지금까지 Agent Skill이 Tool Use 위에서 어떻게 동작하는지 알아보았다.

Skill 도구가 tools 배열에 정의되어 있고 tool_use → tool_result 사이클을 거친다. 이는 Subagent(Task 도구)나 MCP(mcp__xxx 도구)와 동일한 패턴이다.

tools 배열
├── 내장 도구 (Bash, Read, Glob...)
│   └── Host 내부에서 직접 실행

├── Task 도구 (Subagent)
│   └── 새 메시지 루프에서 LLM 응답 반환

├── mcp__xxx 도구 (MCP)
│   └── 외부 서버의 실행 결과 반환

└── Skill 도구 (Skills)
    └── SKILL.md 로드 후 후속 도구 사용 안내

Agent Skill은 다른 도구 사용을 안내하는 메타 도구다. tool_result로 지시문을 컨텍스트에 주입하고 이후 Bash, Read 같은 다른 도구들의 사용을 안내한다. 결국 Skill → Bash → Read... 형태의 도구 체이닝이 발생한다.

Subagent와 MCP가 "무엇을 할 수 있는가"를 확장한다면 Skills는 "어떻게 잘 할 것인가"를 확장한다.

Read more →
7
1