Meta, Valve의 Steam Deck용으로 설계된 Linux 스케줄러를 대규모 서버에 사용
------------------------------
- Valve의 Steam Deck를 위해 설계된 *SCX-LAVD 리눅스 스케줄러* 가 Meta의 대규모 서버 환경에서도 효과적으로 작동한다는게 공개됨
- 이 스케줄러는 *게임 콘솔 수준의 효율적 자원 관리* 를 목표로 설계된 것인데, Meta는 이를 통해 *서버 워크로드의 성능 향상* 과 *지연 시간 최소화* 를 추구
- 휴대용 게…
------------------------------
https://news.hada.io/topic?id=25293&utm_source=googlechat&utm_medium=bot&utm_campaign=1834
洪 民憙 (Hong Minhee)
@hongminhee@hackers.pub · 1004 following · 710 followers
Hi, I'm who's behind Fedify, Hollo, BotKit, and this website, Hackers' Pub! My main account is at
@hongminhee洪 民憙 (Hong Minhee)
.
Fedify, Hollo, BotKit, 그리고 보고 계신 이 사이트 Hackers' Pub을 만들고 있습니다. 제 메인 계정은:
@hongminhee洪 民憙 (Hong Minhee)
.
Fedify、Hollo、BotKit、そしてこのサイト、Hackers' Pubを作っています。私のメインアカウントは「
@hongminhee洪 民憙 (Hong Minhee)
」に。
Website
- hongminhee.org
GitHub
- @dahlia
Hollo
- @hongminhee@hollo.social
DEV
- @hongminhee
velog
- @hongminhee
Qiita
- @hongminhee
Zenn
- @hongminhee
Matrix
- @hongminhee:matrix.org
X
- @hongminhee
iOS에서 내가 쓸 단모음 키보드 앱을 만들고 있다.
기존에 쓰던 앱들이 있긴 했는데, 하나는 어느샌가 키보드 자판 위에 툴바를 붙이더니 월 구독 결제를 안하면 숨길 수 없게 해놨고 다른 하나는 최근 iOS 업데이트 이후로 텍스트를 지울때 단어가 망가지는 문제가 생겨서 대체제를 찾다가 마음에 드는게 없어서 결국 직접 만들기로 결정함..
직접 만들어서 테스트하다보니 유저 입장에서 어떤 부분이 불편한지 - 해소해야할지 보인다. 재미는 있는데 생각보다 신경써야 할 게 많다.
그런데 실제로 이를 조건 분기로 구현해보면 상당히 복잡해진다. 더 일반화된 방법이 필요하다. 버전 관계를 다시 정의해보자. 변환기를 기준으로 생각해보면 모든 버전 사이에 순서가 있다고 할 수는 없다. 버전을 poset으로 모델링해보면 어떨까? a ≺ b 관계가 성립하는 경우에는 b의 변환기를 이용해 a를 마이그레이션할 수 있다. 이 관계가 성립하려면 (1) a가 안정 버전이고, (2) b가 알파 버전이 아니고, (3) major(a) < major(b)이어야 한다.
(1.x, 3.x)는 모든 조건을 만족하기 때문에 변환기를 거쳐 마이그레이션할 수 있다. (1.x, 3.x-beta)도 모든 조건을 만족한다. 반면 (3.x-beta, 1.x)는 (1), (3)을 위반하기 때문에 변환기를 사용할 수 없다. (2.x-alpha.1, 3.x-alpha.0)는 (1), (2)를 위반한다. 이렇게 출발 버전부터 시작해 a ≺ b 관계를 만족하고, b가 목적 버전이 아닌 경우 안정 버전이어야 한다는 조건을 적용해 나가면 도착 버전까지의 경로도 얻을 수 있다.
오랜만에 복잡한 문제를 단순한 형태로 모델링해서 해결했다. 문제 상황은 대략 라이브러리의 메이저 버전을 위해 자동 마이그레이션을 지원하는 것이다. 각 메이저 버전에는 이전 버전의 코드를 자동으로 변환해주는 변환기가 함께 배포된다. 만약 1.x에서 3.x로 업데이트할 때는 2.x 변환기, 3.x 변환기를 순차적으로 실행해야 한다. 즉, 하위 버전에서 출발해 상위 버전에 도착하는 경로를 구해야 한다.
그런데 베타 버전(x.y-beta)과 알파 버전(x.y-alpha.z)을 고려해야 한다. 베타 버전에는 직전 버전에 대한 변환기가 있고, 알파 버전에는 변환기가 없다. 나이브하게 생각해보면 출발 버전 유형과 도착 버전 유형의 조합에 대해 모든 경우를 분기해서 경로를 구할 수 있을 것처럼 보인다.
그런데 실제로 이를 조건 분기로 구현해보면 상당히 복잡해진다. 더 일반화된 방법이 필요하다. 버전 관계를 다시 정의해보자. 변환기를 기준으로 생각해보면 모든 버전 사이에 순서가 있다고 할 수는 없다. 버전을 poset으로 모델링해보면 어떨까? a ≺ b 관계가 성립하는 경우에는 b의 변환기를 이용해 a를 마이그레이션할 수 있다. 이 관계가 성립하려면 (1) a가 안정 버전이고, (2) b가 알파 버전이 아니고, (3) major(a) < major(b)이어야 한다.
오랜만에 복잡한 문제를 단순한 형태로 모델링해서 해결했다. 문제 상황은 대략 라이브러리의 메이저 버전을 위해 자동 마이그레이션을 지원하는 것이다. 각 메이저 버전에는 이전 버전의 코드를 자동으로 변환해주는 변환기가 함께 배포된다. 만약 1.x에서 3.x로 업데이트할 때는 2.x 변환기, 3.x 변환기를 순차적으로 실행해야 한다. 즉, 하위 버전에서 출발해 상위 버전에 도착하는 경로를 구해야 한다.
그런데 베타 버전(x.y-beta)과 알파 버전(x.y-alpha.z)을 고려해야 한다. 베타 버전에는 직전 버전에 대한 변환기가 있고, 알파 버전에는 변환기가 없다. 나이브하게 생각해보면 출발 버전 유형과 도착 버전 유형의 조합에 대해 모든 경우를 분기해서 경로를 구할 수 있을 것처럼 보인다.
Found this helpful resource by Ben Boyter (@boyter): a collection of sequence diagrams explaining how #ActivityPub/#WebFinger works in practice—covering post creation, follows, boosts, deletions, and user migration.
If you're trying to implement ActivityPub, the spec can be frustratingly vague, and different servers do things differently. This aims to be a “clean room” reference for getting federation right.
On a related note, if you learn better by building things, @fedifyFedify: ActivityPub server framework has a step-by-step tutorial where you create a federated microblog from scratch—implementing actors, inbox/outbox, following, and posts along the way:
Found this helpful resource by Ben Boyter (@boyter): a collection of sequence diagrams explaining how #ActivityPub/#WebFinger works in practice—covering post creation, follows, boosts, deletions, and user migration.
If you're trying to implement ActivityPub, the spec can be frustratingly vague, and different servers do things differently. This aims to be a “clean room” reference for getting federation right.
GUI앱을 못보니까 콘솔로 출력해서 본다는 점이 정말... 나보다 낫군
According to
@tchambersTim Chambers's My 2026 Open Social Web Predictions:
Fedify will power the federation layer for at least one mid-sized social platform (500K+ users) that adds ActivityPub support in 2026. The “build vs. buy” calculation for federation shifts decisively toward “just use Fedify.”
We're honored by this recognition and will keep working hard to make #ActivityPub adoption easier for everyone. Thank you, Tim!
Here's a #TypeScript API design challenge I'm working on: adding async support to #Optique (CLI argument parser) without breaking the existing sync API.
The tricky part is combinators—when you compose parsers with object() or or(), the combined parser should automatically become async if any child parser is async, but stay sync if all children are sync. This “mode propagation” needs to work at both type level and runtime.
I've prototyped two approaches and documented findings. If you've tackled similar dual-mode API designs, I'd love to hear how you approached it.
목공용 절단도면 만들어주는 프로그램 뇌 비우고 Claude Code한테 시켰더니 때깔이 좋네
Stacked Diff 관리하려고 Sapling을 찍먹해봤는데, 이건 누가 hands on 세션을 하는게 아닌 이상 익숙해지는게 좀 어려울 듯.....
https://github.com/stadia/textsearch_ko 잘 쓰고 있는 pg 확장인데 너무 오래 된 것 같아 업데이트를 해봤습니다. 원본 https://github.com/i0seph/textsearch_ko
洪 民憙 (Hong Minhee) shared the below article:
스마일 PRO 라식 수술 후기
자손킴 @jasonkim@hackers.pub
시력교정술을 받은 주변사람들이 신세계라며 추천을 해도 그동안 관심이 없었다. 어렸을때부터 안경을 쓰고 평생을 살아온지라 안경을 쓰는 것이 불편하다고 느껴 본 적이 없었기 때문이다.
라식을 해볼까? 라고 생각이 든 것은 스쿠버 다이빙을 시작하게 되면서였다. 다이빙에 취미를 붙이고나니 가장 불편한게 눈이었다. 렌즈를 끼고 있는게 불편한건 물론이고 아침 바쁜 와중에 렌즈가 안들어가서 진을 다 빼고 하루를 시작하는것도 문제였다. 게다가 렌즈를 부족하게 들고가거나 숙소에 렌즈를 놓고 오는 등 나의 정신머리 때문에 반쪽짜리 다이빙을 하게 되는 일이 반복되면서 시력교정을 해야겠다고 결심했다.
공장식 병원이야 어차피 거기서 거기라는 생각에 병원비교 사이트를 보고 적당한 곳을 골라 검사 예약을 했다. 원하면 당일 수술도 가능하다고 하던데 나는 검사만 먼저 받고 일주일후에 수술을 하기로 했다.
검사는 일반적인 안과 검사와 크게 다른 것은 없었다. 눈에 바람을 쏘는 것을 시작으로 뭔가를 들여다보고 빛을 비추고 등등 예닐곱가지의 검사가 진행되었다. 이어서 시력 검사를 하고 교정 후 도수를 결정했는데 이것은 안경 맞출때와 동일했다.
검사가 끝나고 의사선생님과 상담하며 눈의 상태에 대해서 설명을 들었다. 다행히 안압이나 눈 모양등도 정상이고 각막 두께도 평균이라 라섹, 라식 모두 원하는대로 진행이 가능하다고 했다. 다만 이제 노안이 오고 있기 때문에 돋보기를 쓰는 시간을 조금이라도 더 늦추려면 시력을 약간 낮추고 양눈 중 주로 가까이 보는 눈은 시력을 조금 더 낮춰 교정하는게 좋겠다는 제안을 받았다.
이어서 코디네이터에게 수술 종류와 방법에 대한 설명을 들었다. 나는 회복이 빠른게 최우선이었기 때문에 스마일PRO를 하기로 결정했다.
수술 당일에도 2~3가지의 검사를 다시하고 최종적으로 교정 시력에 대해서도 다시 한 번 확인을 하고 코디네이터에게 수술 후 주의사항과 안약 투여에 대한 설명을 듣는다.
눈에 물이 닿는 것과 격렬한 운동은 일주일 정도 피해야한다. 2주간은 금주하고 한 달동안 과음도 피해야 한다. 목욕탕, 사우나처럼 뜨거운 증기와 물은 한달간 피한다.
의사선생님을 만나 눈 상태에 대해서 최종점검을 하고 수술 대기실로 이동을 하여 위생모와 가운을 입고 잠시 기다리다 수술실로 들어갔다. 간호사님의 안내에 따라 수술장비에 누우면 눈을 감고 있으라 한 후 세팅을 시작하는데, 이때부터는 절대 고개를 들지말라고 한다.
잠시후 눈을 뜨라고 한 뒤 의사가 눈에 마취안약을 넣어줬다. 집게 같은걸로 눈을 벌리고 "이것만 문제 없으면 다른건 잘 참으실 수 있을거에요"라는 소리와 함께 눈앞에서 뭔가가 왔다갔다 하는데 아마도 마취가 되었는지 안구를 건드려 보는 것 같았다. 눈에 아무런 느낌은 없었다.
수술은 오른쪽 눈부터 진행되었다. 왼쪽눈에는 거즈 같은 것을 덮고 오른쪽 눈으로 앞에 보이는 초록불빛을 바라보라고 한다. 초록불빛을 바라볼때 다른 눈을 감으면 눈이 움직일 수 있으니 양쪽눈을 다 뜨되 수술하는 눈으로만 보라고 한다.
처음에는 크고 흐릿하게 보였던 초록불빛이 점점 작고 선명해 지면서 초록점으로 보인다.초록점을 눈의 중앙에 오도록 보라고 하는데, 이때가 가장 어려웠다. 초록점을 눈의 가운데에 오도록 보고 있는데 의사가 거기가 아니니 제대로 보라고 하는것이다. 몇 번을 다시 시도해도 잘 안됐는지 "환자분이 하는 수술인데 협조가 안되면 어떻게 하냐"며 의사가 약간 역정을 냈다.
초록점을 보는데 실패하면 기계가 눈을 잡아줄 수 없고 의사가 직접 조작하여 수술을 해야 하는데, 그러면 아무래도 기계만큼의 정확도가 나오지는 않는 모양이다.
어떻게하면 방향을 맞출 수 있을지 궁리하다 눈알을 오른쪽에서 왼쪽으로 천천히 굴려보다 의사가 됐다고 하면 멈춰보기로 했다. 눈알을 굴리는데 의사가 거기가 맞다고 하였다. 초록점은 안구의 중앙이 아닌 미간 정도의 위치에 있었다. 잘은 몰라도 사람마다 안구의 각도 같은게 차이가 있나보다.
이제 레이저를 조사하니 가만히 있으라 하고 의사와 간호사가 시간을 세어준다. 초록점을 보고 6~7초 정도 있으면 어느새 점이 사라지고 눈앞이 하얗게 보인다. 2~3초가 더 지나자 끝났다며 잘 참았다고 한다. 레이저가 조사되는 동안은 별다른 느낌은 없었다.
다음으로 왼쪽눈을 진행하는 오른쪽보다 훨씬 수월했다. 왼쪽눈도 우선 처음에는 초록점을 눈의 중앙에 오게 바라봤는데 그게 맞았는지 한 번에 진행되었다. 마찬가지로 약 10초정도가 걸려 레이저 조사가 끝났다.
레이저 조사가 끝나면 무언가로 눈을 후비적거리고 주사를 몇 개 놓고 안약등을 넣고 불빛을 쬐어준다. 이것은 레이저 조사가 마지막으로 끝난 왼쪽을 먼저 하고 오른쪽을 하였다. 아마도 각막 조각을 제거하고 소독등의 처치를 하는 것 같았다. 이 과정은 눈 한쪽당 1~2분 정도 걸렸던 것 같다.
모든 과정이 끝나면 간호사의 안내에 따라 장비에서 일어나 이동을 한다. 궁금한 마음으로 주변을 둘러봤는데 세상이 온통 뿌연게 온통 손자국이 번짐 안경을 쓰고있는 기분이었다. 뿌옇긴해도 이전보다 더 선명해진것은 체감이 됐다.
다시 한 번 의사선생님을 만나 눈에 이상이 없나 검사를 받고 몇가지 주의 사항을 들은 후 퇴원을 한다. 초반에는 빛번짐이 있을 수 있고 시력이 한번에 교정시력 만큼까지 잘보이는 건 아니지만 시간이 지나며 계속 더 잘보일 것 이라고 한다. 그리고 이제 눈이 시리기 시작할건데 1~3시간 정도면 가라앉을 거라는 이야기도 들었다.
퇴원하는 길에 약국에 들려서 안약 2종류와 인공눈물을 받았다. 안약은 일주일간 하루 4번을 투여하고 인공눈물은 수시로 넣으라고 한다. 안약을 여러개 넣을때는 최소한 5분 간격을 두고 넣으라고 하는데 종류가 여러개이다보니 안약 넣다보면 하루가 다 간다.
집에 오니 눈시림이 더 심해지고 눈에 다래끼나 나거나 눈썹이 들어간듯한 이물감이 느껴져서 눈을 뜨고 있을 수가 없었다. 눈을 떠도 온통 뿌옇게 보여서 사물의 형체는 분간이 되지만 글씨 같은건 읽을 수가 없었다. 안약을 넣어야 하는데 주의사항이 적혀있는 종이를 읽을 수 없어서 제미나이 라이브를 켜고서 읽어달라고 했다.
안약을 넣고서 침대에 누워 눈을 감았다. 눈을 감아도 빛이 밝으면 눈이 시려서 빛을 차단하고 누워있다 두어시간 자고 일어났다. 이물감은 여전했지만 눈시림과 빛번짐이 덜해서 눈을 뜨고 무언가를 볼 수는 있었다. 눈을 오래 뜨고 있으면 피로감이 있는건 마찬가지라 저녁 먹고 다시 안약을 넣고 일찍 잠을 청했다. 덕분에 밀린 수면 부채를 많이 갚았다.
눈의 피로감 때문인지 깊게는 못자고 자다 두어번 깼다. 일어나보니 새벽 5시쯤 되었는데 더이상 잠이 안오길래 후기나 써야겠다고 생각했다. 이물감이나 눈시림은 많이 나아졌고 눈도 어제보다는 더 선명하게 보이기 시작했다. 그러나 빛번짐으로 인해 탁하고 뿌옇게 보이는건 여전했다.
모니터나 스마트폰의 화면을 볼 수는 있는데 밝으면 눈이 아프고 집중하면 눈이 시려서 글자 크기를 키우고 화면 밝기는 최대한 낮췄다. 모니터를 오래 보면 눈이 금방 피로해져서 드문드문 후기를 적다가 진료 시간이 다되어 다시 병원을 찾았다.
라식 수술 후에는 1일, 1주일, 1개월, 3개월에 진료를 받는 것을 권장하고 6개월 이후에는 6개월~1년 주기로 한 번씩 검사를 받는 것을 권장한다고 한다.
불편한게 있는지 물어봐서 눈시림과 이물감이 있었지만 지금은 많이 좋아졌다고 답하였다. 먼거리와 가까운 거리의 시력을 다시 한 번 검사하고 의사선생님을 만나 안구 상태에 대해서 진료를 받았다. 어제 수술 중 초록점을 바라보는 문제에 대해 이야기를 나눴다.
내가 걱정했던 부분은 혹여나 잘못된 위치를 바라봐서 각막이 엉뚱하게 절삭된 것은 아닐까 하는 것이었다. 하루가 지나면서 그렇지는 않을 것 같다는 생각을 했으나 혹시 모를 일이라 한 번 더 의사선생님한테 물어봤다.
다행히 어제 수술도 정확히 되었고 오늘 안구 상태도 이상 없으니 걱정 말라는 답을 들었다. 혹여나 초록점을 제대로 못보는 환자가 있다면 나처럼 안구의 중앙이 아닌 다른 곳을 봐야 하는 것일 수 있으니 다른 곳을 보도록 유도하며 안구를 맞추면 좋겠다는 말씀을 드리고 다음주에 뵙자하고 진료를 마무리했다.
시간이 지날수록 시야가 점점 선명해 지는게 체감이 되고 있다. 그러나 뿌옇게 보이는 느낌은 아직 남아 있어서 안경을 닦거나 고쳐써야 할 것 같은데 그럴 안경이 없어서 당혹스러움을 느끼고 있다.
나는 이제 지금까지와는 다른 눈으로 세상을 보게 되었다.
CI 실행 시간을 1/3로 줄였음
GitHub Workflows 어렵네.. 3년 동안 새로운 걸 배워보려고 하지 않은 업보가 이렇게. 즐거운 마음으로 해야 하는데 ㅎㅎ
NextJS 처음 써보는데, 챗봇 UI처럼 인터액티브한 웹앱을 만들때 도움이 되는 부분이 뭔지를 모르겠다. 처음엔 SPA + API 서버 만드는것과 비교해, 자명한 데이터 바인딩 보일러플레이트를 줄여줄거라 생각했다. 근데, 순수 SSR로 처리할 수 없는, 클라에서 상태를 업데이트하는 약간만 복잡한 플로우에서도 전혀 도움이 안된다.
일하다 막힐 때 위키피디아, 한국민족문화대백과사전, man 페이지 설명 읽는게 머리 식히는데 좋은 것 같음
[구인 커피챗 요청] 2025-12-26 ~ 2026-01-06 한국에 잠시 방문하는데, 좋은 개발자 분들을 만나고 싶습니다.
저는 펜시브 라는 미국 교육 AI 스타트업 CTO이고, 최근에 크게 투자유치를 하여 현재 초기 개발팀을 꾸리고 있습니다.
- 미국비자 지원받고 바로 샌프란시스코로 넘어오고 싶은 개발자 (미국에서 일하셔야 합니다!)
- 하루종일 학습에 대해서 생각하고 싶은 개발자
- 작은 팀으로 데카콘을 만들고 싶은 개발자
커피챗 연락주십시오: >> minjune@pensieve.co <<
펜시브 제품소개: https://claude.com/customers/pensieve
기술스택: typescript + react / fastapi + python / firebase / postgres
정보가 부족한 에러 메시지로 인해서 약 하루를 버렸다. 정보가 부족한 것도 있지만, 믿음(?)이 부족해서 멀쩡한 곳만 잔뜩 쳐다보고 있었다.
Claude Code에 네이티브 LSP 지원 기능 추가
------------------------------
- 터미널에서 실행되는 *AI 기반 코딩 도구* 인 Claude Code 가 최신버전에서 *LSP (Language Server Protocol) 도구* 를 추가
- 이를 통해 *정의로 이동(go-to-definition)* , *참조 찾기(find references)* , *호버 시 문서 표시* 같은 *IDE 수준의 코드 인텔리전스 기능* 제공
- /terminal-setup 명…
------------------------------
https://news.hada.io/topic?id=25269&utm_source=googlechat&utm_medium=bot&utm_campaign=1834
NextJS + Vercel AI SDK로 챗봇 UI 만들다가 수명이 줄겠네..
State of the Art가 별건가… 서령은 예술의 경지가 맞다. 입이 귀에 걸린 채로 먹고 온 것 같다. 잘 먹었습니다!!
오픈소스 프로젝트의 커뮤니티를 어떻게 운영해야 지속적으로 사람들이 참여하게 할 수 있을까..
@bglbgl gwyng Nix의 대안으로 Guix도 종종 거론되던데, 혹시 살펴보신 적 있으실까요?
@hongminhee洪 民憙 (Hong Minhee) 비슷한 접근이란 것만 알고 있습니다. 남은 문제를 개선하는데 어느쪽이 더 좋은 설계를 갖고있는지는 모르겠네요. 근데 Nix가 일종의 기본 레지스트리에 해당하는 nixpkgs의 규모가 더 클거 같은데, 그러면 당장 쓰기엔 수고가 덜할거 같네요.
코드 수정 제안 반영은 해당 줄을 대치하는 방식으로 처리해도 충분했는데 이제 그래프 매칭 문제로 바꿔야겠다
그동안 Nix 쓰면서 고통도 많이 받았는데 그래도 주변에 꾸준히 Nix를 권한다. Nix가 '빌드'라는 소프트웨어 개발의 아주 일반적인 문제를 한방에 푸는 방법론이기 때문이다. 물론 아직 몇가지 문제가 좀 있지만(잘 안된다, 불편하다 식의 단순한 문제는 아니다), 나 자신도 그 해결책을 위한 몇가지 아이디어를 가지고 있고, 머지않은 미래에 풀릴거라 생각한다.
UNIX 철학이 작은 기능을 확실하게 수행하는 프로그램들을 만들어 조합하자인데, ls, cat, grep 등이 그 예시다. 내가 볼땐 Nix도 생각도 거기에 해당된다. Nix도 좋은 의미로 의외로 꽤 작다.
어제 송년회에서 오라클 이야기가 너와서 나도 써보려고 하는데... n8n이라도 올려야겠다.
Optique 문서를 보다가 argument ordering 파트에서 프로퍼티가 나타난(? appear) 순서대로 파서가 동작(? consume)한다고 되어 있어서 Object 타입인데 이게 작성한 순서대로 Object.entries() 같은 곳에서 순회되기를 기대할 수 있나 의문이 들었다(Object가 Map같은 거라고 생각했어서).
아래와 같이 타고 가면:
20.1.2.5 Object.entries ( O )(User call)7.3.23 EnumerableOwnProperties ( O, kind )(called by2. Let entryList be ? EnumerableOwnProperties(obj, key+value).10.1.11 [[OwnPropertyKeys]] ( )(called by1. Let ownKeys be ? O.[[OwnPropertyKeys]]().)10.1.11.1 OrdinaryOwnPropertyKeys ( O )(called by1. Return OrdinaryOwnPropertyKeys(O).)
아래와 같은 대목을 만나는데:
- Let keys be a new empty List.
- For each own property key P of O such that P is an array index, in ascending numeric index order, do
- Append P to keys.
- For each own property key P of O such that P is a String and P is not an array index, in ascending chronological order of property creation, do
- Append P to keys.
- For each own property key P of O such that P is a Symbol, in ascending chronological order of property creation, do
- Append P to keys.
- Return keys.
만약 key가 array index가 아닌 문자열 혹은 Symbol이라면 프로퍼티 생성 발생의 오름차순 순서(? ascending chronological order of property creation)대로 순회(?)해야한다고 적혀있다.
아마.. 잘 못 찾아서 못 본 걸수도 있지만 chronological이나 creation 같이 검색했을때 스펙에서 이를 다루는 방법을 정의하지는 않는 것 같았다. 예를 들어, PropertyDescriptor이 auto increment 되는 고유 ID를 갖고 있어야 하고 이를 통해 정렬해야한다, 거나?
실제 구현을 보고 싶어서 GitHub에 있는 V8 미러로 가서 보니 key들을 OrderedHashSet으로 갖고 있는 듯 했다. 생각해보니 그러면 되네, 싶어서 더는 안 찾아봤다.
암튼 Optique 문서대로 생성 순서대로 동작할 것 같다!
가족들이 밋업 굿즈를 탐내는 건 처음이다. 다음번엔 앵그리 버전 한 표!
洪 民憙 (Hong Minhee) shared the below article:
미소녀 보려고 미연시를 켰더니 게임 콘솔이 해킹당했어요
Helloyunho @helloyunho@hackers.pub
당신은 게임에 갇힌 미소녀들을 보기 위해 PlayStation을 켰습니다. 마침 친구가 "나 이쪽 루트 클리어했는데 너도 볼래?" 라며 세이브 파일을 주네요. 마침 그 루트로 갈 시기를 놓친 당신은 친구의 세이브를 이용해 확인해보려고 합니다. 세이브를 등록 후, 미연시를 켜서 로드했는데..? 갑자기 콘솔이 멈추며 결국 콘솔 내부 저장공간을 포맷했습니다!
... 당연히 위 내용은 실제 스토리가 아니지만, 충분히 일어날 수 있습니다. 이 글에서 설명할 내용들로 말이죠.
yarpe
yarpe(Yet Another Ren'Py PlayStation Exploit)를 소개합니다!
이 스크립트는 Ren'Py 기반의 PlayStation 게임들에서 적용되는 취약점이며, 현 시점에서 적용 가능한 게임은 다음과 같습니다:
- A YEAR OF SPRINGS PS4 (CUSA30428, CUSA30429, CUSA30430, CUSA30431)
- Arcade Spirits: The New Challengers PS4 (CUSA32096, CUSA32097)
그런데, 이 모든 것이 어떻게 시작되었고, 어떻게 만들어진걸까요?
Xbox One/Series
사실 저는 PlayStation(이하 PS로 줄여서 말하겠습니다)에 관심이 없었습니다. 반대로 Xbox에 많은 관심이 있었죠. 처음 Xbox One/Series의 커널 취약점이 생겼을 때 Warhammer: Vermintide 2의 게임 세이브 취약점을 이용한 게임 덤프가 제 눈에 잡혔습니다. 그때 문뜩 든 생각이: "다른 게임은 이런 세이브 취약점이 없을까?" 였는데요, 저와 같이 이런 작업에 관심을 두는 친구가 먼저 추천해준 것은 RPG Maker(쯔꾸르 라고도 많이 불리죠)로 만들어진 게임들이었습니다. 아쉽게도, 콘솔 버전에서 사용하는 RPG Maker 게임들은 다른 세이브 구조를 가지고있었고, ACE(Arbitrary Code Execution)가 불가능했습니다.
그러다 문뜩 생각이 났습니다: "Ren'Py 게임들이 세이브로 Pickle을 사용하지 않나?"
Pickle
Python에는 Pickle이라는 직렬화(serialization) 방식이 존재합니다. 이는 Python의 (왠만한) 모든 object를 직렬화할 수 있는 특징이 있습니다.
하지만 만약 직렬화하지 못하는 object가 class의 property로 존재하는데, 이 class를 직렬화하고 싶다면 어떻게 해야할까요? Python은 이를 위해 __reduce__라는 method를 지원합니다. 이는 class가 직렬화/역직렬화 될 때 어떤 데이터를 사용하여 class를 다시 구성해야할지 명시해줍니다.
사용법은 다음과 같습니다:
class A:
def __init__(self, a):
self.a = a
self.b = "b"
def __reduce__(self):
return self.__class__, (self.a,)
# serialize
a = A()
b = pickle.dumps(a)
그런데, 만약 __reduce__에 다른 Python 함수가 있으면 어떨까요? 예를 들어, exec 같은거라면 말이죠?
class Exploit:
def __reduce__(self):
return exec, ("print('Hello, World!'),)
exploit = Exploit()
a = pickle.dumps(exploit)
pickle.loads(a) # Hello, World!
...네, Pickle이 로딩될 때 문자열에 담긴 코드를 실행해버립니다... 이것이 Python 공식 Pickle 문서에서 Pickle이 안전하지 않다고 하는 이유겠죠.
세이브 하나로 Ren'Py 게임 가지고 놀기
이제 Ren'Py가 Pickle을 사용한다는 사실과, Pickle로 코드를 실행할 수 있다는 사실을 알았으니, 직접 실행해볼 시간입니다!
Ren'Py의 세이브는 1-1-LT1.save 같은 파일 이름을 가지고 있습니다. 멋져보이지만, 사실 그냥 Zip 파일이며, 확장자만 .save로 변경된겁니다.
이를 흔한 Zip 프로그램으로 풀어보면, 여러 파일들이 나오지만 우리가 관심있는 파일은 log 파일입니다. 이 파일이 Ren'Py의 Pickle을 담고있는 파일이죠.
이제 이 파일을 제가 만든 코드가 담긴 Pickle로 바꿔치기 하고, 다시 압축을 해서 넣으면..?

코드 실행이 됩니다! 너무 멋지네요!
코드 실행은 되는데, 이제 어쩌죠?
이제 코드 실행이 되는걸 알았으니, 다음 단계는 무엇일까요? 당연히 메모리 조작이죠! Google에서 잠시 조사한 결과 unsafe-python 이라는 저장소가 눈에 들어왔습니다. 이 저장소는 Python에서 직접적인 메모리 접근을 가능하게 합니다.
해당 취약점은 LOAD_CONST opcode가 아무 범위 검사를 하지 않는다는 점을 이용하여 가짜 PyObject를 만들 수 있고, 이를 이용하여 0부터 사실상 64비트 주소 끝자락까지의 bytearray 객체를 만들어 직접적인 메모리 접근을 합니다.
이제 우리는 메모리 주소만 알면 언제든지 해당 메모리를 수정할 수 있습니다! 덤으로, Python의 사랑스러운 slicing 문법은 이를 더 편하게 만듭니다.
# Assume we got raw memory bytearray
mem = getmem()
mem[0x18000000:0x18000008] = b'\0' * 8
이제 마음대로 메모리 조작도 가능하고, PyObject 생성도 가능하니, 저만의 프로그램을 메모리에 저장한 후 Python의 function 객체를 만들어 제 코드를 향하게 하면 끝입니다!
...가 된다면 정말 쉬울건데 말이죠...
메모리 영역 권한
메모리 영역에는 특정 권한이 부여되어 있습니다. Read, Write, eXecute 권한이 분리되어 있는데, 이름에서 알 수 있듯 execute 권한 없이는 해당 메모리 영역을 코드로서 실행할 수 없습니다.
문제되는 부분은, 보통 우리가 작성하는 영역은 read와 write만 있고, execute 권한이 없습니다! 만약 execute 권한이 없는 영역을 실행하려 한다면, CPU에서 권한 부족 오류를 발생시킬 것이고, 이는 segfault로 이어질 것입니다.
그럼 현재 부족한 메모리 권한으로 원하는 명령을 어떻게 실행할 수 있을까요? 답은 ROP에 있습니다.
ROP
ROP, Return Oriented Programming은 말 그대로 asm의 ret 명령을 기준으로 작동하는 코드를 말합니다.
ret 명령의 특징은 현재 CPU가 가리키는 stack pointer(x86_64 기준 RSP register) 에 적힌 주소 값을 instruction pointer(x86_64 기준 RIP register)에 적고 stack pointer를 움직인다는 것입니다. 그럼 ret를 실행하는 때에 stack pointer를 (실행 가능한 메모리 영역에 있는) 저희가 원하는 코드로 향하게 하면 어떨까요?
이를 하기 위해선, ret로 끝나면서 원하는 명령을 실행하는 메모리 주소를 미리 찾아놓아야 할 것입니다. 이를 우리는 gadget이라고 부릅니다.
Stack pointer에서도 권한 오류가 발생할 수 있지 않을까 하실 수도 있지만, stack pointer가 가리키는 메모리 영역은 read, write 권한만으로 충분하기 때문에 괜찮습니다.
이 사실을 알게된다면 이제 이런 구상을 할 수 있습니다:
- Python list를 통해 custom stack을 만든다.
- Custom stack에는 적절히 gadget을 배치한다.
- Stack pointer를 원하는 주소(여기선 Python list의 elements 주소)로 변경하는 gadget을 향하도록 한 Python function 객체를 만든다.
- 해당 Python function 객체를 실행한다. Stack pointer가 옮겨지고
ret가 호출되며 원하는 명령이 실행된다!
...많은 것들이 축약되있지만 대략적으로 이런 구상이 가능하죠. 이제 이를 이용해서 취약점을 만들 시간입니다!
Gadget 찾기
앞서 말했듯 ROP를 하기 위해선 적절한 gadget을 찾는 것이 중요합니다. 저는 이를 위해 ROPgadget 툴을 이용했습니다. 원하는 executable과 함께 툴을 실행하면 ret로 끝나는 모든 asm 명령들을 메모리 주소 값과 함께 찾아줍니다! (가상 메모리 주소까지 고려해서요!)
다음엔 두 가지 방법이 있습니다:
- Executable 메모리를 읽으며 gadget 주소를 동적으로 찾기
- 미리 해당 gadget들의 주소를 적어둔
dict만들기
Xbox One/Series에선 1번 방법을 사용할 수 있었지만, PS에선 후에 언급할 내용 때문에 2번 방법을 쓸 수 밖에 없었습니다.
Stack pointer를 원하는 주소로 옮기기
이제 stack pointer를 만들어둔 Python list 주소로 옮기면 되는데, 어떻게 옮길까요? 저희가 원하는건 (x86_64 기준) mov rsp, ???와 ret입니다. 여기서 저 ???부분이 중요한데, 왜냐하면 Python function 호출이 어떻게 이루어지는지 알아야하며, 실행되는 CPU와 OS의 함수 호출 convention도 알아야하기 때문입니다.
여기서 함수 호출 convention이란 함수를 호출할 때 몇번째 argument가 어떤 register에 들어가는지를 뜻합니다.
Linux/UNIX 기반 OS의 x86_64 함수 호출 convention 순서는 다음과 같습니다: RDI, RSI, RDX, RCX, R8, R9
그리고 Python function 호출은 다음과 같이 이루어집니다:
function_call(PyObject* func, PyObject *arg, PyObject *kw)
따라서 만약 mov rsp, [rdi + 0x30]; ret 라는 명령을 찾았다면, 직접 만드는 Python function 객체 안 0x30 정도 되는 곳에 원하는 stack 주소를 넣어야할 것이고, mov rsp, [rsi + 0x10]; ret 라는 명령을 찾았다면, 직접 tuple 객체를 만든 후 0x10 정도 되는 곳에 stack 주소를 저장, 만든 function 객체를 부를 때 my_func(*custom_tuple)과 같이 호출해야 할 것입니다.
다 만들었으니 실행하면 되는데... Python으로 못 돌아오고 crash?
ROP에서 가장 중요한걸 깜빡했네요. 직접 만든 stack을 실행하고 나선 다시 원래 stack으로 돌아와야겠죠.
저같은 경우는 push rbp; mov rbp, rsp; xor esi, esi; call [rdi + 0x130] 명령을 이용하여 rbp에 rsp를 저장한 후 원하는 명령을 실행하도록 만들었습니다(rdi + 0x130에는 stack pointer를 변경하는 명령이 있습니다).
이 다음 원하는 명령 실행 후 mov rsp, rbp; pop rbp; ret 명령을 통해 다시 원래 stack pointer로 돌아옵니다.
이렇게만 하면 될까요..? 아닙니다. 이렇게 하면 Python이 함수의 return value(x86_64 기준 RAX register)를 참조하려다 잘못된 값을 참조하여 오류가 발생합니다. 그럼 어떻게 해야할까요?
정답은 None 객체를 반환해주는 것입니다. 이렇게 하면 Python에게 정상적인 값을 반환하게 되며, 오류가 발생하지 않게 됩니다. (그리고 네, None도 하나의 객체입니다.)
주의할 점은 None 객체의 refcount를 1만큼 올려주어야 합니다. 그렇지 않으면 Python이 return value의 refcount를 줄이려 할 때, underflow 문제가 발생할 수 있습니다.
이것까지 마치면, 진짜로 저희가 원하는 명령을 실행할 수 있게 됩니다!
Xbox에서 테스트!
Xbox One Research 팀의 도움을 받아 Ren'Py 게임 파일을 받은 뒤 gadget을 찾고, 돌려봤습니다!

Xbox에서 먼저 테스트한 결과 정상적으로 socket을 여는데 성공했으며, 해당 socket으로 다른 Python script를 실행하는 데 성공했습니다! (참고로 해당 게임은 Python의 socket 모듈을 지원하지 않습니다.)
Xbox 같은 경우 Windows와 거의 비슷한 함수를 사용할 수 있어서 편하게 진행할 수 있었습니다.
대망의 PS...
그렇게 Xbox에서 테스팅 후 몇달 뒤, PS 해킹에도 관심이 생겨 알아보게 되었습니다.
그렇게 알게된 Xbox와의 차이점은...
- FreeBSD 기반의 OS를 사용함
- 자체적인 syscall들이 존재함
- 메모리에 올라간 실행 파일에는 ELF 해더가 없음(Import table 알 수 없음)
- 실행 파일에 기록된 모듈만 로드할 수 있음
- PS5 기준: 실행 파일이 담긴 메모리 영역을 읽을 수 없음(XOM)
...Gadget 찾기에서 2번 방법을 사용한 이유가 XOM(eXecutable Only Memory) 때문입니다. 사실 PS4에선 1번 방법을 사용할 수 있지만, 저는 PS5 게임도 지원하고 싶었습니다.
PS5 Research & Development Discord 서버의 도움을 받아 게임 파일을 받았고, 똑같이 gadget을 찾아 작성하였습니다.
위에 적힌 제약들이 있어도, 기본 작동은 비슷하기 때문에 큰 문제 없이 만들 수 있었고, 그렇게 테스트를 한 결과..!

성공적으로 작동되었고, yarpe가 탄생할 수 있었습니다.
마무리
여기까지 오는데 (중간에 쉬었지만) 거의 1년이라는 시간이 걸렸습니다. 만들면서 힘든 것 보단 재밌다는 느낌을 더 많이 받았네요. (만드는 동안은 잠자는 시간마저 줄여가며 만들었던 것 같습니다.)
마무리하기 전에, 저에게 도움이 되었던 분들을 소개하며 끝내고자 합니다.
- Xbox One Research 팀: 이 프로젝트의 시작점이 되어주었으며, 핵심 부분을 구성하는데 큰 도움이 되었습니다. (tuxuser, LukeFZ, Billy, harold님 등이 도와주셨습니다.)
- Dr.Yenyen: PS4/5 게임들의 파일을 제공해주셨고, 많은 테스트를 진행해주셨습니다.
- Gezine: 취약점을 개발하며 제가 궁금했던 부분이나 잘못된 부분을 답변/지적 해주셨습니다.
- Sajjad: Dr.Yenyen님과 함께 많은 테스트를 진행해주셨습니다.
- cow: 직접 파일 대조까지 해주시며 문제가 되는 부분을 고쳐주셨습니다.
- earthonion: 테스트를 진행해주셨으며 많은 조언을 해주셨습니다.
긴 글 읽어주셔서 감사합니다.
tauri로 패스워드 툴 만드는 이유, 레거시 프로덕트의 지속 여부를 결정할 때 llm을 어찌 썼는지, claude skill 활용 방법, 오라클 클라우드 쓰면 왜 좋은가, 개발자가 개발을 좋아하냐, 좋아 해야만 하냐, 개발자의 ethic 등... 2025년 라스트 개발 밋업이 알차네요.
LLM 도움이면 못할 것도 없을 듯 하여 블로그를 직접 만들고 싶은데, 스프링부트 + 그냥JS로 만들어도 괜찮을지 모르겠습니다. (사실 아는 게 그것 뿐입니다..) 혹시 조금 더 편리하고 좋은 스택이 있을까요?
지금 만드는 걸 좀 어떻게든 해야 보이든 할 텐데. UK Gov의 컴포넌트를 굉장히 참고하고 있습니다. 'looking good!'을 신경쓰다보면 쉬운 길은 많죠. 장애인 관련 홈페이지를 만들고 있습니다.
어제 해커스펍 송년회를 잘 다녀왔습니다. 이동하는 버스 안에서 제 해커스펍 주소를 QR 코드로 열심히 만들었는데, 막상 공유할 용기는 없어서 못 했네요 ㅋㅋ
전날까지도 갈까 말까 고민을 많이 했었는데, 부담감을 이기고 다녀오길 정말 잘했다는 생각이 들었습니다. 라이트닝 토크도 하나같이 유익했어요.
저는 클로드 코드는 아직 써보지 않았는데 (토큰이 살살 녹는다는 얘기를 들어서요 ㅋ ), 이야기를 듣다 보니 요즘 대세는 확실히 클로드 코드인가 싶더라고요. 이제 써볼까 생각만 하고 있었는데, 어제 라이트닝 토크 발표를 듣고 나니 “바로 시작해봐야겠다”는 생각이 들었습니다. 🐱💧
집에서 멀어서 좀 늦게 도착. 아무튼 왔다.
역시 전반적으로 클로드를 많이들 쓰시는구나- 하는 인상
해커스펍 송년회 인기 폭발이라 참여를 못하고 튕겨져 나왔어요 ㅎㅎ. 서로 말귀!가 통하는 사람들끼리 네트워킹 시간을 가질 수 있는 귀한 기회였습니다.
해커스펍 송년회를 다녀왔습니다. 발표를 라이트하게 가져간다 해서 라이트하게 질문 편하게 했는데, 질문 총량이 넘어가진 않았나 걱정될 정도로 많이 한 것같아 살짝 불안하지만, 재밌었습니다.
이미지를 리사이징, 다른 포맷으로 변환하는데, imagemagick은 올바르게 이미지를 처리하는데, imgage-rs, mozjpeg는 알 수 없는 점이 잔뜩 생긴다. 일단 되는 것 찾았으니...
https://github.com/makachanm/knife
개인적으로 진행하는 풀스택 프로젝트로써 단순한 싱글-유저 액티비티펍 블로그 플랫폼인 Knife를 제작중이다. 직접 이 플랫폼으로 글을 가끔 끼적이면서 조금씩 개선시킬 생각이다.
제 지인 분이 GitHub 에서 인종차별적 코멘트를 받으셨습니다. GitHub 계정이 있으시면 신고 부탁드립니다. 영어가 어려우시더라도 LLM으로 신고글 써달라고 하면 잘 써줍니다. 신고는 단 시간 내에 많이 찍혀야 실제 보고로 올라가기 때문에 가능하신 분들은 꼭 신고 부탁드립니다.
오늘 해커스펍 송년회에서 오라클 사용해도 될까? 주제로 발표한 로빈입니다! 행사 정말 재밌었고요 오라클 쓰는 이야기 GraphQL 이야기 플랫폼 비즈니스의 방향성 이야기 등등 마음껏 해주세요~
오늘 Hackers' Public @ Seoul 송년회에서 이야기한 "코딩 에이전트와 함께 이세계 던전 탐험하기: 새로운 환경의 코드 베이스를 빠르게 분석하는 방법"의 발표 슬라이드는 [다음 링크]에서 다운로드 받으실 수 있습니다! :)
RISC-V Mainboard For the Framework Laptop 13 Is Now Available https://hardware.slashdot.org/story/25/02/04/217248/risc-v-mainboard-for-the-framework-laptop-13-is-now-available?utm_source=rss1.0mainlinkanon
오늘 Hackers' Public @ Seoul 송년회에서 "개발자는 개발을 좋아해야 하는가? 에 대한 고찰"로 라이트닝 토크를 진행했습니다. 사실 제가 발표자인줄도 모르고 앞에서 이야기를 진행하게 되었는데, 많은 분께서 호응해주시고 또 경험에서 우러나온 진솔한 의견을 내주셔서 다양한 이야기가 오갈 수 있었습니다. 모두 감사드리며, 내년에 모두 원하시는 바 이루기를 기원하겠습니다. 🥰
Just had someone leave feedback on my F/OSS project saying “maybe that's fine if a product is focused on your Chinese community.”
I'm Korean. Every single piece of documentation is in English. There's nothing in Chinese anywhere in the project.
This kind of microaggression is exhausting. As a non-white maintainer, you deal with these assumptions constantly—people who feel entitled to your labor while casually othering you based on your name.
It chips away at your motivation. It makes you wonder why you bother.
https://github.com/dahlia/optique/issues/59#issuecomment-3678606022
루비(Ruby)의 홈페이지가 새 단장했다고 해서 들어가봤는데 꽤 예쁜데? https://www.ruby-lang.org/ko/
洪 民憙 (Hong Minhee) shared the below article:
Subagent는 Tool Use입니다.
자손킴 @jasonkim@hackers.pub
지난 글에서는 Tool Use가 무엇이고 어떻게 동작하는지 알아보았다. 이번 글에서는 subagent가 Tool Use 위에서 어떻게 동작하는지 알아볼 것이다.
Tool Use 정리
Subagent 설명에 앞서 Tool Use를 간단히 정리해보고 넘어가자.
┌─────────────────────────────────────────────────────────────┐
│ 메인 메시지 루프 │
│ │
│ ┌──────┐ request ┌─────────┐ API call ┌─────┐ │
│ │ User │ ─────────────► │ Agent │ ────────────► │ LLM │ │
│ │ │ ◄───────────── │ │ ◄──────────── │ │ │
│ └──────┘ response │ │ tool_use └─────┘ │
│ │ │ │ │
│ │ ▼ │ │
│ ┌──────────────┐ │
│ │ Tools │ │
│ │ (Bash, Read, │ │
│ │ Glob, ...) │ │
│ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
Tool Use는 LLM이 외부 도구를 호출할 수 있게 해주는 메커니즘이다. Agent는 LLM에게 사용 가능한 도구 목록을 제공하고 LLM은 필요할 때 tool_use 응답을 반환한다. Agent는 해당 도구를 실행하고 결과를 다시 LLM에게 전달한다. 이 과정이 반복되면서 복잡한 작업을 수행한다.
Subagent란?
Subagent는 특정 작업에 특화된 AI 에이전트다. 각 subagent는 자신만의 컨텍스트 윈도우에서 독립적으로 작동하며 완료되면 결과를 메인 에이전트에게 반환한다.
Claude Code에서 subagent는 Task라는 도구로 구현되어 있다. 메인 에이전트의 시스템 프롬프트에는 Task 도구의 description으로 사용 가능한 모든 subagent 목록과 각각의 용도가 포함된다. 메인 에이전트는 이 description을 참고하여 적절한 subagent를 선택한다. 예를 들어 "코드베이스 구조 파악"이 필요하면 Explore를, "데이터베이스 스키마 설계"가 필요하면 database-schema-architect를 호출한다.
Subagent의 구조
Subagent는 마크다운 파일로 정의되고 YAML frontmatter에 다음과 같은 설정을 포함한다.
---
name: code-reviewer
description: Expert code review specialist. Use immediately after writing or modifying code.
tools: Read, Grep, Glob, Bash
model: sonnet
---
You are a senior code reviewer ensuring high standards of code quality and security.
When invoked:
1. Run git diff to see recent changes
2. Focus on modified files
3. Begin review immediately
Review checklist:
- Code is simple and readable
- Functions and variables are well-named
...
각 필드의 역할은 다음과 같다:
| 필드 | 설명 |
|---|---|
name |
subagent의 고유 식별자 |
description |
언제 이 subagent를 사용해야 하는지 설명 |
tools |
이 subagent가 사용할 수 있는 도구 목록 |
model |
사용할 모델 (sonnet, opus, haiku 또는 inherit) |
YAML frontmatter 아래의 마크다운 본문이 subagent의 시스템 프롬프트가 된다. 이 프롬프트는 subagent가 호출될 때 API 요청의 system 필드에 주입되어 subagent의 행동을 정의한다.
이 subagent 정의는 메인 에이전트의 시스템 프롬프트에 있는 tools 섹션의 Task 도구 설명에 포함된다:
{
"name": "Task",
"description": "Launch a new agent to handle complex, multi-step tasks autonomously.
Available agent types and the tools they have access to:
- Explore: Fast agent specialized for exploring codebases. (Tools: All tools)
- Plan: Software architect agent for designing implementation plans. (Tools: All tools)
- database-schema-architect: Use this agent when you need to design database schemas...
...
"
}
Task 도구의 input schema는 다음과 같다:
{
"type": "object",
"properties": {
"subagent_type": {
"type": "string",
"description": "The type of subagent to launch"
},
"prompt": {
"type": "string",
"description": "The task to delegate to the subagent"
},
"description": {
"type": "string",
"description": "Short description of what the agent will do"
}
}
}
subagent_type: 호출할 subagent 종류 (Explore, Plan, general-purpose 등)prompt: subagent에게 전달할 작업 지시. 이 내용이 subagent의 user 메시지가 된다description: 3-5단어의 짧은 요약. 로그나 UI에 표시된다
Subagent 호출 과정
실제 메시지 흐름을 통해 subagent가 어떻게 동작하는지 살펴보자.
1단계: 메인 에이전트가 Task 도구 호출
사용자가 "선물을 주고받는 기능을 위한 entity 설계"를 요청했다고 하자. 메인 에이전트(Opus 4.5)는 먼저 코드베이스를 탐색해야 한다고 판단하고 Explorer subagent를 호출한다:
{
"content": [
{
"type": "thinking",
"thinking": "사용자가 선물을 주고받는 기능을 위한 entity 설계를 요청했습니다. Plan mode가 활성화되어 있으므로, 먼저 코드베이스를 탐색하여 기존 구조를 이해해야 합니다.\n\nPhase 1에 따라:\n1. 기존 entity 구조와 패턴을 이해하기 위해 Explore agent를 사용\n2. 선물 관련 기존 구현이 있는지 확인\n3. 사용자와 아이템 관련 entity가 어떻게 구성되어 있는지 파악\n\n먼저 Explore agent를 사용하여 기존 entity 구조를 파악하겠습니다."
},
{
"type": "text",
"text": "선물 주고받기 기능을 위한 entity 설계를 시작하겠습니다. 먼저 기존 코드베이스의 entity 구조와 관련 패턴을 탐색하겠습니다."
},
{
"type": "tool_use",
"id": "toolu_01QjShG7gnF36vLgGKCWBNjg",
"name": "Task",
"input": {
"subagent_type": "Explore",
"prompt": "이 NestJS TypeScript 프로젝트에서 entity 구조를 탐색해주세요:\n\n1. 기존 entity 파일들의 위치와 구조 파악...",
"description": "Entity 구조 탐색"
}
}
]
}
thinking 블록에서 메인 에이전트는 현재 상황(Plan mode 활성화)을 파악하고 Explore agent를 선택한 이유를 명시적으로 추론했다.
2단계: 새로운 메시지 루프에서 Subagent 실행
Agent는 Task 도구 호출을 받으면 완전히 새로운 메시지 루프를 시작한다. 이 때 위에서 보았던 YAML frontmatter 하단의 마크다운 본문이 subagent 전용 시스템 프롬프트로 주입된다.
{
"model": "claude-haiku-4-5-20251001",
"system": [
{
"type": "text",
"text": "You are Claude Code, Anthropic's official CLI for Claude."
},
{
"type": "text",
"text": "You are a file search specialist for Claude Code...
=== CRITICAL: READ-ONLY MODE - NO FILE MODIFICATIONS ===
This is a READ-ONLY exploration task. You are STRICTLY PROHIBITED from:
- Creating new files
- Modifying existing files
- Deleting files
...
Your strengths:
- Rapidly finding files using glob patterns
- Searching code and text with powerful regex patterns
- Reading and analyzing file contents
..."
}
],
"messages": [
{
"role": "user",
"content": "<system-reminder>\nAs you answer the user's questions, you can use the following context:\n# claudeMd\nCodebase and user instructions are ... <system-reminder>"
},
{
"role": "user",
"content": "이 NestJS TypeScript 프로젝트에서 entity 구조를 탐색해주세요..."
}
],
"tools": [
{"name": "Bash", ...},
{"name": "Glob", ...},
{"name": "Grep", ...},
{"name": "Read", ...}
]
}
- 모델: Explorer는 빠른 탐색을 위해 Haiku 4.5를 사용한다 (메인은 Opus 4.5)
- 시스템 프롬프트: 읽기 전용 모드로 제한된 전용 프롬프트가 주입된다
- 도구 제한: 메인 에이전트가 가진 모든 도구가 아닌, 탐색에 필요한 도구만 제공된다
- CLAUDE.md 전달: 프로젝트의
CLAUDE.md내용이 user 메시지의<system-reminder>태그 안에 포함되어 전달된다. 따라서 subagent도 프로젝트 컨벤션을 따른다 - 메시지: 메인 에이전트의 prompt가 user 메시지로 전달된다
3단계: Subagent의 도구 사용
Subagent도 자신만의 메시지 루프에서 도구를 사용한다:
{
"content": [
{
"type": "text",
"text": "이 NestJS 프로젝트의 entity 구조를 철저하게 탐색하겠습니다."
},
{
"type": "tool_use",
"id": "toolu_01VHysdohmuy9r58aVdcUQRh",
"name": "Glob",
"input": {
"pattern": "**/*.entity.ts"
}
},
{
"type": "tool_use",
"id": "toolu_01QYrMBhzbXpExYwgCFM7MLb",
"name": "Bash",
"input": {
"command": "find /Volumes/project/src -type f -name \"*.ts\" | grep -E \"(entity|entities)\" | head -20",
"description": "Find entity files in src directory"
}
}
]
}
Explorer subagent는 두 개의 도구를 병렬로 호출했다. Glob으로 entity 파일 패턴을 찾고 Bash로 디렉토리를 탐색한다. 이 과정이 반복되면서 subagent는 필요한 정보를 수집한다.
4단계: Subagent 완료 및 결과 반환
Subagent가 탐색을 완료하면 수집한 정보를 정리하여 최종 응답을 반환한다. 이 응답은 메인 에이전트의 Task 도구 호출에 대한 tool_result로 전달된다:
┌─────────────────────────────────────────────────────────────────────┐
│ 메인 메시지 루프 │
│ │
│ User ──► Agent ──► LLM(Opus) │
│ │ │
│ ▼ tool_use: Task │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Subagent 메시지 루프 (Explorer) │ │
│ │ │ │
│ │ prompt ──► Agent ──► LLM(Haiku) │ │
│ │ │ │ │
│ │ ▼ tool_use │ │
│ │ ┌─────────┐ │ │
│ │ │ Glob │ │ │
│ │ │ Bash │ │ │
│ │ │ Read │ │ │
│ │ └─────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 최종 응답 │ │
│ └────────────────────┬────────────────────────────────┘ │
│ │ │
│ ▼ tool_result │
│ Agent 계속 진행 │
│ │
└─────────────────────────────────────────────────────────────────────┘
메인 에이전트는 Explorer의 조사 결과를 바탕으로 다음 단계(예: database-schema-architect 호출)를 진행한다.
마무리
지금까지 subagent의 동작 방식에 대해서 알아보았다. 핵심을 정리하면:
Subagent는 Tool Use다. 메인 에이전트가 Task 도구를 호출하면, 새로운 메시지 루프가 생성되어 전용 시스템 프롬프트와 제한된 도구로 작업을 수행한다.
왜 사용하는가?
- 컨텍스트 분리: 탐색/분석 과정이 메인 대화를 오염시키지 않는다
- 전문화: 각 subagent가 특정 작업에 최적화된 프롬프트와 도구를 가진다
- 효율성: 목적에 맞게 가벼운 모델(Haiku)이나 무거운 모델(Opus)을 취사선택
주의할 점:
- Subagent는 메인 대화 히스토리를 모른다. 필요한 정보는 Task의 prompt에 명시해야 한다 (단,
CLAUDE.md는 자동 전달됨) - Subagent는 subagent를 호출할 수 없다 (무한 중첩 방지를 위해 Task 도구를 사용하지 않는다.)
- 각 호출은 새로운 컨텍스트에서 시작한다 (단, resume 파라미터로 이전 대화 이어가기 가능)
결국 subagent는 Tool Use 패턴의 확장이다. 단순한 Function Call이라면 코드 실행 결과를 텍스트로 반환하지만 subagent는 별도의 메시지 루프에서 LLM이 생성한 텍스트를 반환한다는 차이만 있다. 메인 에이전트 입장에서는 둘 다 tool_result로 받는 텍스트일 뿐이다.














