러스트로 갈아타기 (릴리 마라, 조엘 홈즈 (지은이), 전봉규 (옮긴이) / 한빛미디어 / 2026-01-30 / 35,000원) https://feed.kodingwarrior.dev/r/0Q9ijP
https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=384921506&partner=openAPI&start=api
@kodingwarrior@hackers.pub · 694 following · 508 followers
Neovim Super villain. 풀스택 엔지니어 내지는 프로덕트 엔지니어라고 스스로를 소개하지만 사실상 잡부를 담당하는 사람. CLI 도구를 만드는 것에 관심이 많습니다.
Hackers' Pub에서는 자발적으로 바이럴을 담당하고 있는 사람. Hackers' Pub의 무궁무진한 발전 가능성을 믿습니다.
그 외에도 개발자 커뮤니티 생태계에 다양한 시도들을 합니다. 지금은 https://vim.kr / https://fedidev.kr 디스코드 운영 중
러스트로 갈아타기 (릴리 마라, 조엘 홈즈 (지은이), 전봉규 (옮긴이) / 한빛미디어 / 2026-01-30 / 35,000원) https://feed.kodingwarrior.dev/r/0Q9ijP
https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=384921506&partner=openAPI&start=api
1월 24일 (토) 배포 예정인 Deck v0.13.0을 미리 살짝 보여 드립니다
최근 그냥 코딩 에이전트에게 코딩을 넘어서 삶의 귀찮은 자동화나 일들을 대신 해주는 느낌으로도 쓸 수 있지 않을까 싶어서, 자는 동안 남은 토큰 한도로 epub 파일로 된 소설을 번역시켰더니 꽤 그럴싸한 수준의 번역이 나와서 놀랐다. 그래서 이건 좀 잘 만들어두면 굉장히 편하겠다 싶어서 pdf 파일과 epub 파일을 번역하는걸 Agent Skills로 만들어보았다.
Aerofied KDE Plasma
:>
@hongminhee洪 民憙 (Hong Minhee) MoonBit으로 시작하기 좋은 작은 프로젝트들이 있어요 🙂
간단한 CLI 도구, 파서, 혹은 WebAssembly 기반 유틸리티부터 만들어 보셔도 좋아요.
예제와 문서는 공식 사이트에서 확인하실 수 있고, 궁금한 점이 있으면 언제든지 말씀 주세요!
직사각형에서 시작점이 고정이고 휴리스틱 없이 모든 케이스를 풀어야 하는 나이트투어 문제가 드디어 세상에 나왔습니다. 이 문제의 의도한 풀이에 대해서 논문을 쓸 계획도 있긴 한데, 저 문제에 대해서만 쓰면 좀 심심할 거 같아서 아즈텍 다이아몬드 같은 데에 적용하는 것도 생각 중입니다.
RE: https://mastodon.social/@cheeaun/115751067284225457
#PhanpySocial by
@cheeaunChee Aun 🤔 continues to be my favorite Mastodon web client. This new feature is just one of many reasons why.
https://phanpy.social/#/yip
자신의 사이트에 게시하고, 다른 곳에 동시 배포하기
------------------------------
- *POSSE(Publish on your Own Site, Syndicate Elsewhere)* 는 개인 사이트에 먼저 게시한 뒤, 소셜미디어 등 외부 플랫폼에 복제본이나 링크를 배포하는 *콘텐츠 자율 배포 방식*
- 이 방식은 *콘텐츠 소유권과 원본 URL* 을 유지하면서도, *친구나 팔로워가 사용하는 플랫폼에서 접근* 할 수 있게 함
- POSSE…
------------------------------
https://news.hada.io/topic?id=25529&utm_source=googlechat&utm_medium=bot&utm_campaign=1834
The future is plain text files — it's just not evenly distributed
Jaeyeol Lee shared the below article:
소피아 @async3619@hackers.pub
X-Frame-Options 의 악몽에서 깨어나세요, 프록시 서버 개발기 X-Frame-Options? 그게 뭔가요? 우리가 아는 몇몇 대형 웹 서비스들(유튜브 등)은 보통의 경우 다른 웹 사이트에서 iframe 요소를 통해 임베딩 되는 것을 거부하지 않습니다. 다만, 몇몇 웹 서비스는 다른 웹 페이지에서 iframe 요소를 통해 표시되길 거부합니다. 제품 정책 및 보안상의 이유로 표시를 거부하는 목적이 있겠습니다.
이러한 니즈를 충족시킬 수 있는 HTTP 헤더가 X-Frame-Options 입니다. 이 헤더의 값을 SAMEORIGIN 내지는 DENY로 설정하면, 직관적인 값에 따라 알맞게 프레임 내 임베딩 가능 여부를 결정할 수 있습니다.

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

웹 페이지 내에 다른 웹 페이지가 임베딩 되어 '미리보기' 처럼 제공되는 경험을 보신 적이 있으신가요? Omakase AI는 자사 프로덕트의 데모를 위와 같이 제공하고 있습니다. 캡쳐하여 실시간으로 전송되는 영상의 화면 위에, 자사 컨텐츠를 올려두어 실제 적용 시에 어떤 네러티브를 제공할지 데모 형식으로 보여줍니다.
문제는 이 모든 경험이 영상을 통해 진행 된다는 점 입니다. 영상 전송은 필연적으로 지연 시간이 존재할 수 밖에 없습니다. 여러분이 스크롤을 내리고, 클릭을 하는 등의 작은 인터렉션 하나가, 큰 지연 시간 뒤에 처리가 된다고 하면 유저는 답답하고 매끄럽지 않음을 느낄 것이고 이는 곧 이탈로 이어질 가능성이 있습니다. (구글은 비슷한 맥락에서 Core Web Vitals로써 INP를 설명하고 있습니다)
따라서 저는 영상을 보내는 나이브한 방법 이외의 유저의 브라우저에서 외부 웹 서비스를 표시할 좋은 방법을 찾아야 했고, 그것이 바로 프레임 내지는 iframe을 사용하는 방법 이었습니다. 이런 맥락에서 X-Frame-Options를 우회할 필요가 생긴 것 입니다.

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

이 중간자 역할을 하는 프록시 서버를 중간에 두어 요청을 모두 프록시 서버를 거치도록 하는 방법을 이용하는 것 입니다. 간단하게는 X-Frame-Options 응답 헤더의 제거가 있을 것 입니다. 중간자가 X-Frame-Options 헤더를 제거함으로 응답을 수신하는 클라이언트 브라우저의 iframe 요소는 큰 문제 없이 내용을 표시할 수 있게 됩니다.
처음에는 그저 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에 대한 개념의 도입이 필요 했습니다. 상대 경로 진입에도 안정적으로 작동할 수 있는 새로운 방식의 접근이 필요 했습니다. 저는
를 생각해 냈어야 했고, 그 결과는 이렇습니다. https://section.blog.naver.com/BlogHome.naver?directoryNo=0¤tPage=1&groupId=0 를 예시로 들면, 프록시화 (Proxified) 된 URL은 다음과 같은 것 입니다.
https://example.com/proxy/section/blog/naver/com/_/BlogHome.naver?...
URL hostname의 . 구분자를 /로 치환하고, 이후의 모든 pathname, search 등은 모두 _ 구분자 뒤로 넘김으로서 URL의 원형을 유지할 수 있게 됩니다. 추가적으로 상대 경로 접근에도 안전한 URL을 만들 수 있습니다.
우리는 상대 경로 문제를 해결하기 위해 URL을 프록시화 하는 방법을 사용했고, 이는 제대로 동작하는 듯 해보였습니다. 악몽은 React, Vue 등의 SPA 웹 앱을 프록시하여 표시하는 데에서 시작 되었습니다.
React, Vue 와 같은 프레임워크들은 History API 및 window.location 객체를 기반으로 한 Routing 기능을 제공하고 있습니다.[2] 이 말은, 결국엔 어떤 프레임워크가 되었든 저수준 빌트인 자바스크립트 API를 사용할 수 밖에 없다는 것을 의미 합니다. 그렇다면 직관적으로 생각 해봤을 때,
window(및globalThis) 객체의location속성의 값을 변경해주면 되지 않겠나?
라고 생각할 수 있습니다. 그러나 이는 불가능 합니다.

어떠한 이유 때문인지는 알 길이 없었지만, 자바스크립트는 그렇게 만만한 존재가 아니었습니다. 다른 좋은 방법을 찾아야 할 필요가 있었고, 결론에 도달하는 데에는 오랜 시간이 걸리지 않았습니다. 그것은 바로 원격지 웹 페이지에서 실행되는 모든 스크립트의 window.location 객체 접근을 감시하면 어떨지에 대한 아이디어 였습니다.
번들된 소스코드의 경우 대체적으로 다음과 같은 형식을 가지게 됩니다:
const l = window.location;
/* ... */ l.pathname /* ... */
여기서 우리는 변수 l이 window.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을 반환하는 로직 */ }
}
}
// ...
}
글에 열거한 내용 이외에도 정말 많은 기술이 사용 되었는데, 아주 재밌는 경험 이었습니다. 혹여나 이러한 비슷한 기능을 하는 기능을 개발할 일이 있으시다면, 도움이 됐으면 좋겠습니다.
여기서 거의 라는 표현을 사용한 이유는, 제 짧은 식견에서 보자면 비슷한 맥락에서 사용하는 기법으로 integrity 속성이 있을 수 있겠습니다. ↩︎
예를 들면, location.pathname을 읽어 현재 Route가 어떤 Route인지 감지하는 등의 동작이 있겠음. ↩︎
2026년 병오년 새해를 맞아 식탁보 1.16.0 버전을 출시했습니다. 이번 버전에서는 폴더 마운트 기능, 그리고 백그라운드 비동기 다운로드를 구현하여 이전보다 최대 30~40% 이상 빨라진 환경 구축 속도를 달성했습니다.
코딩 AI 어시스턴트의 도움을 받아 계속해서 빠른 출시와 적극적인 기능 반영을 이어 나가도록 하겠습니다. 많은 공유와 후원을 계속 부탁드리겠습니다!
#식탁보 #인터넷뱅킹 #NPKI #보안 #플러그인 #공동인증서
https://github.com/yourtablecloth/TableCloth/releases/tag/v1.16.0
최근 보안의 화두는 제로트러스트지만, 정작 가장 민감한 공동인증서 파일은 여전히 NPKI라는 고전적인 디렉터리 구조 속에 노출되어 있습니다.
OS 수준의 암호화 기술인 BitLocker나 VBS의 이점을 전혀 활용하지 못하는 현 상황을 개선해보고자, Windows 인증서 저장소를 백엔드로 활용하는 방식을 고민 중입니다. macOS의 Keychain 시스템처럼 인증서를 시스템 보안 영역 내부로 끌어들임으로써, 파일 탈취 위험을 획기적으로 낮추는 것이 목표입니다.
인프라 자체를 바꿀 순 없어도, 엔드포인트 단에서 '방어의 밀도'를 높이는 유의미한 시도가 될 것 같습니다. :-D
Jaeyeol Lee shared the below article:
자손킴 @jasonkim@hackers.pub
Agent Skill은 Anthropic이 2025년 10월에 발표한 기능이다. 발표 직후부터 폭발적인 반응을 얻어 커뮤니티에서 다양한 종류의 Skill이 만들어졌다. 2025년 12월 18일에 Anthropic은 Agent Skills를 독립적인 오픈 스탠다드로 발표했고 여러 서비스들이 Skill을 지원하고 있다.
이번 글에서는 Agent Skill이 Tool Use 위에서 어떻게 동작하는지 알아본다.
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을 활성화할 수 있게 한다.
일반적인 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는 선택 필드다. 각 선택 필드의 역할은 다음과 같다.
YAML frontmatter 아래의 마크다운 본문이 실제 지시문이 된다. 이 지시문은 Skill이 활성화될 때 컨텍스트에 주입되어 에이전트의 행동을 안내한다.
첫번째 단계에서는 frontmatter의 name과 description만 사용된다. 이 정보로 에이전트는 언제 이 Skill을 활성화해야 하는지 판단한다. 두번째 단계에서 SKILL.md 전체가 로드되고 세번째 단계에서 마크다운 본문의 지시문에 따라 scripts/ 폴더의 코드를 실행하거나 references/ 폴더의 추가 문서를 참조한다.
이제 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를 읽고 지시문에 따라 다른 도구들을 사용하면서 발현된다.
Subagent와 Agent Skill은 서로 다른 문제를 해결한다.
Subagent는 컨텍스트 분리가 필요할 때 사용한다. 탐색이나 분석 과정이 메인 대화를 오염시키면 안 될 때 적합하다. 예를 들어 코드베이스 전체를 탐색해야 하는데 그 과정의 모든 파일 내용이 메인 컨텍스트에 쌓이면 금방 컨텍스트가 소진된다. Subagent는 독립적인 컨텍스트 윈도우에서 작업하고 결과만 반환한다. 또한 가벼운 모델(Haiku)로 빠르게 처리하거나 무거운 모델(Opus)로 깊이 분석하는 선택이 가능하다.
Agent Skill은 절차적 지식이 필요할 때 사용한다. PDF 폼 채우기나 브라우저 자동화처럼 "어떻게 해야 하는지"에 대한 베스트 프랙티스가 있는 작업에 적합하다. Skill은 현재 컨텍스트를 공유하면서 지시문만 추가로 주입한다. 별도의 메시지 루프를 만들지 않는다.
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는 "어떻게 잘 할 것인가"를 확장한다.
루비, 레일즈에서 페디버스를 구현하려면 https://gitlab.com/experimentslabs/federails 이 프로젝트가 구현 정도가 잘 되어 있으나 2명이서 틈틈히 개발하고 있어서 진행 상황이 느린 상태. 컨트리뷰터가 되어야 하나 포크를 해야 하나...
Jaeyeol Lee shared the below article:
자손킴 @jasonkim@hackers.pub
지난 글에서는 Subagent가 Tool Use 위에서 어떻게 동작하는지 알아보았다. 이번 글에서는 MCP(Model Context Protocol)가 Tool Use와 어떻게 연결되는지 내장 도구인 Subagent를 예시로 비교하며 설명할 것이다. 또한 내장 도구가 있음에도 불구하고 MCP가 필요한 이유에 대해서도 알아본다.
Subagent 글에서 살펴본 Task 도구는 에이전트에 내장된 도구였다. MCP 도구는 어떻게 다를까? 결론부터 말하면 LLM 입장에서는 둘 다 그냥 도구다. 차이는 실행이 어디서 일어나는가뿐이다.
내장 도구든 MCP 도구든 API 요청의 tools 배열에 동일한 형태로 들어간다:
{
"tools": [
{
"name": "Read",
"description": "Reads a file from the local filesystem...",
"input_schema": { ... }
},
{
"name": "Task",
"description": "Launch a new agent to handle complex tasks...",
"input_schema": { ... }
},
{
"name": "mcp__claude-in-chrome__navigate",
"description": "Navigate to a URL in the browser...",
"input_schema": { ... }
}
]
}
LLM은 도구 이름과 description, input_schema만 보고 어떤 도구를 호출할지 결정한다. 이 도구가 내장인지 MCP인지는 알 수 없고 알 필요도 없다.
핵심 차이는 도구가 어디서 실행되는가다.
| 내장 도구 (예: Task) | MCP 도구 | |
|---|---|---|
| 실행 위치 | Host 내부 | Host 외부 (별도 프로세스) |
| 통신 방식 | 함수 호출 | 프로토콜 (STDIO/HTTP) |
| 실행 주체 | Host (또는 LLM) | 외부 시스템 |
| 결과 | Host가 생성한 데이터 | 외부 시스템이 반환한 데이터 |
다이어그램으로 보면 더 명확하다:
Host 프로세스
─────────────────────────────────────────────────
Agent
│
┌────────────┴────────────┐
▼ ▼
Task mcp__xxx 도구
(내장 도구) │
│ │
▼ │ STDIO / HTTP
새 메시지 루프 │
(LLM) │
│
─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│─ ─ ─ ─ ─ ─
│
외부 프로세스 ▼
MCP Server
(Chrome, DB...)
지난 글에서 본 Explorer subagent 호출과 MCP 도구 호출을 비교해보자.
내장 도구 (Task) 호출:
{
"type": "tool_use",
"id": "toolu_01ABC123",
"name": "Task",
"input": {
"subagent_type": "Explore",
"prompt": "entity 구조를 탐색해주세요",
"description": "Entity 구조 탐색"
}
}
Task 도구가 호출되면 Host 내부에서 새로운 메시지 루프가 생성되고, Haiku 모델이 Glob, Read 등 다른 내장 도구로 탐색을 수행한다. 결과는 LLM이 생성한 분석 텍스트다.
MCP 도구 호출:
{
"type": "tool_use",
"id": "toolu_01DEF456",
"name": "mcp__claude-in-chrome__navigate",
"input": {
"tabId": 12345,
"url": "http://localhost:3000"
}
}
MCP 도구가 호출되면 Host는 외부의 MCP Server(Chrome 브라우저 프로세스)에 명령을 전달한다. 결과는 브라우저가 반환한 데이터(스크린샷, 콘솔 로그 등)다.
Agent 입장에서는 둘 다 tool_use 요청을 받아 실행하고 tool_result를 반환하는 동일한 패턴이다.
MCP 서버가 제공하는 도구들은 어떻게 tools 배열에 들어갈까? MCP 도구는 mcp__server-name__tool-name 형태의 이름을 가진다.
{
"name": "mcp__claude-in-chrome__navigate",
"description": "Navigate to a URL, or go forward/back in browser history...",
"input_schema": {
"type": "object",
"properties": {
"tabId": {
"description": "Tab ID to navigate",
"type": "number"
},
"url": {
"description": "The URL to navigate to",
"type": "string"
}
},
"required": ["tabId", "url"]
}
}
이 네이밍 규칙이 필요한 이유는 여러 MCP 서버가 동시에 연결될 수 있기 때문이다. 예를 들어 filesystem 서버와 github 서버가 둘 다 read라는 도구를 제공한다면 충돌이 발생한다. mcp__filesystem__read와 mcp__github__read로 구분하면 이 문제가 해결된다.
Claude Code에서 /mcp를 입력하면 연결된 MCP 서버 목록을 볼 수 있다. Claude in Chrome이 제공하는 도구들을 살펴보자:
| 도구 이름 | 설명 |
|---|---|
mcp__claude-in-chrome__navigate |
URL로 이동하거나 브라우저 히스토리 앞/뒤로 이동 |
mcp__claude-in-chrome__computer |
마우스/키보드로 브라우저와 상호작용, 스크린샷 촬영 |
mcp__claude-in-chrome__read_page |
페이지의 접근성 트리 표현을 가져옴 |
mcp__claude-in-chrome__find |
자연어로 페이지 요소 찾기 |
mcp__claude-in-chrome__form_input |
폼 요소에 값 입력 |
mcp__claude-in-chrome__javascript_tool |
페이지 컨텍스트에서 JavaScript 실행 |
이 도구들은 에이전트가 MCP 서버에 연결할 때 서버로부터 목록을 받아와 tools 배열에 추가된다. 에이전트가 시작될 때 대략 다음과 같은 과정이 일어난다:
tools/list 요청을 보내 제공하는 도구 목록 수신mcp__server-name__ prefix를 붙여 tools 배열에 추가Claude가 MCP 도구를 호출하면 에이전트는 다음 단계를 수행한다:
Claude Agent MCP Server
│ │ │
│ tool_use │ │
│ (mcp__claude-in- │ │
│ chrome__navigate) │ │
│ ─────────────────────► │ │
│ │ │
│ prefix 파싱 │
│ server: claude-in-chrome │
│ tool: navigate │
│ │ │
│ │ tools/call │
│ │ ───────────────────────► │
│ │ │
│ │ 실행 결과 │
│ │ ◄─────────────────────── │
│ │ │
│ tool_result │ │
│ ◄───────────────────── │ │
│ │ │
결국 MCP 도구 호출도 일반 Tool Use와 동일한 패턴을 따른다. 차이점은 에이전트가 도구를 직접 실행하는 대신 외부 MCP 서버에 위임한다는 것뿐이다.
지금까지 MCP 도구가 어떻게 동작하는지 살펴보았다. 그런데 왜 이런 구조가 필요할까?
에이전트에 도구를 추가하는 가장 단순한 방법은 에이전트 내부에 직접 구현하는 것이다. 하지만 이 방식에는 한계가 있다:
MCP는 이 문제를 도구 제공자와 도구 사용자의 분리로 해결한다.
MCP는 클라이언트-서버 아키텍처를 따른다.
참여자 (Participants):
Host와 Client의 관계:
이 분리 덕분에:
원격 MCP 서버를 사용할 때는 인증이 필요한 상황이 발생한다. MCP 서버가 사용자의 GitHub 저장소에 접근하거나 Slack 워크스페이스에 메시지를 보내야 할 때, "이 요청이 정말 이 사용자로부터 온 것인가?"를 확인해야 한다.
MCP는 프로토콜 수준에서 OAuth 2.1 인증 체계를 표준화했다. 덕분에 어떤 MCP 클라이언트든 동일한 방식으로 MCP 서버에 인증할 수 있고, MCP 서버 개발자는 인증 로직을 한 번만 구현하면 모든 클라이언트와 호환된다.
지금까지 MCP를 Tool Use의 확장으로 설명했다. 실제로 MCP 도구는 가장 많이 사용되는 기능이고, LLM이 외부 시스템과 상호작용하는 핵심 방식이다.
하지만 MCP가 제공하는 것이 도구만은 아니다. MCP 명세를 보면 Tool Use와 무관하게 동작하는 기능들이 있다. 이 기능들은 tool_use -> tool_result 사이클을 거치지 않고 다른 방식으로 LLM에게 컨텍스트를 제공하거나 LLM의 능력을 활용한다.
MCP는 도구(Tools) 외에도 몇가지 핵심 기능들이 있다. Resources, Prompts, 그리고 Sampling이다.
Resources는 Tool Use를 거치지 않는 데이터 제공 기능이다. 도구는 LLM이 "행동"을 요청할 때 호출되지만 Resource는 LLM이 응답을 생성하기 전에 컨텍스트로 미리 주입된다.
예를 들어 PostgreSQL MCP 서버가 데이터베이스 스키마를 Resource로 노출한다고 하자. 사용자가 "users 테이블에 email 컬럼 추가해줘"라고 요청하면 LLM은 별도의 도구 호출 없이도 현재 스키마 구조를 이미 알고 있다. SELECT * FROM information_schema.columns를 먼저 실행할 필요가 없는 것이다. Resource가 컨텍스트에 미리 주입되어 있기 때문이다.
Prompts도 Tool Use와 무관하다. MCP 서버가 미리 정의한 재사용 가능한 프롬프트 템플릿으로 클라이언트가 직접 요청해서 가져온다.
예를 들어 코드 리뷰 MCP 서버가 "보안 취약점 분석" 프롬프트 템플릿을 제공하면 클라이언트는 이 템플릿을 불러와 LLM에게 전달할 수 있다. LLM이 도구를 호출하는 것이 아니라 클라이언트가 MCP 서버로부터 프롬프트를 받아오는 것이다.
Sampling은 가장 독특한 기능이다. 일반적인 MCP 흐름은 LLM → Agent → MCP Server지만 Sampling은 이 방향을 뒤집는다:
일반 흐름: LLM → Agent → MCP Server
Sampling: MCP Server → Agent → LLM
MCP 서버가 복잡한 판단이 필요할 때 역으로 LLM에게 질문할 수 있다. 예를 들어 코드 분석 MCP 서버가 특정 패턴을 발견했을 때 "이 코드가 보안 취약점인지 판단해달라"고 LLM에게 요청하는 식이다. MCP 서버는 LLM의 답변을 기반으로 최종 결과를 만들거나 다른 도구나 함수를 사용하는 등의 판단을 할 수 있게 된다.
Sampling은 Tool Use의 tool_use -> tool_result 패턴이 아니라 MCP 프로토콜 자체의 sampling/createMessage 요청을 통해 동작한다.
지금까지 MCP가 Tool Use 위에서 어떻게 동작하고 또 Tool Use를 넘어 어떤 기능들을 제공하는지 살펴보았다.
MCP 도구는 Tool Use다. 내장 도구와 동일하게 tools 배열에 포함되고 tool_use로 호출되며 tool_result로 결과가 반환된다. LLM 입장에서는 구분이 없다. 차이점은 실행 위치뿐이다. 내장 도구는 Host 프로세스 내부에서, MCP 도구는 외부 MCP Server에서 실행된다.
하지만 MCP는 도구만 제공하지 않는다. Resources와 Prompts는 Tool Use 없이 컨텍스트를 제공하고 Sampling은 MCP 서버가 역으로 LLM을 활용할 수 있게 한다. MCP는 Tool Use를 확장하면서도 Tool Use만으로는 해결할 수 없는 영역까지 커버하는 프로토콜이다.
모든 도구를 에이전트에 내장할 수 없기 때문에 에이전트와 도구를 분리할 필요가 생겼고 MCP는 도구 제공자와 사용자를 분리하여 생태계 확장을 가능하게 한다.
이 글에서는 Tool Use의 기본 구조를, 이어지는 글에서는 Subagent가 Tool Use 위에서 동작함을 살펴보았고 이번 글에서 MCP가 Tool Use를 확장하면서도 그 이상의 기능을 제공함을 살펴보았다. Claude Code의 핵심 확장 기능들은 Tool Use라는 메커니즘 위에서 동작하지만 MCP 생태계는 그보다 더 넓은 가능성을 열어두고 있다.
Jaeyeol Lee shared the below article:
자손킴 @jasonkim@hackers.pub
지난 글에서는 Tool Use가 무엇이고 어떻게 동작하는지 알아보았다. 이번 글에서는 subagent가 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는 특정 작업에 특화된 AI 에이전트다. 각 subagent는 자신만의 컨텍스트 윈도우에서 독립적으로 작동하며 완료되면 결과를 메인 에이전트에게 반환한다.
Claude Code에서 subagent는 Task라는 도구로 구현되어 있다. 메인 에이전트의 시스템 프롬프트에는 Task 도구의 description으로 사용 가능한 모든 subagent 목록과 각각의 용도가 포함된다. 메인 에이전트는 이 description을 참고하여 적절한 subagent를 선택한다. 예를 들어 "코드베이스 구조 파악"이 필요하면 Explore를, "데이터베이스 스키마 설계"가 필요하면 database-schema-architect를 호출한다.
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가 어떻게 동작하는지 살펴보자.
사용자가 "선물을 주고받는 기능을 위한 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를 선택한 이유를 명시적으로 추론했다.
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", ...}
]
}
CLAUDE.md 내용이 user 메시지의 <system-reminder> 태그 안에 포함되어 전달된다. 따라서 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는 필요한 정보를 수집한다.
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 도구를 호출하면, 새로운 메시지 루프가 생성되어 전용 시스템 프롬프트와 제한된 도구로 작업을 수행한다.
왜 사용하는가?
주의할 점:
CLAUDE.md는 자동 전달됨)결국 subagent는 Tool Use 패턴의 확장이다. 단순한 Function Call이라면 코드 실행 결과를 텍스트로 반환하지만 subagent는 별도의 메시지 루프에서 LLM이 생성한 텍스트를 반환한다는 차이만 있다. 메인 에이전트 입장에서는 둘 다 tool_result로 받는 텍스트일 뿐이다.
오픈소스 프로젝트의 커뮤니티를 어떻게 운영해야 지속적으로 사람들이 참여하게 할 수 있을까..
LLM 도움이면 못할 것도 없을 듯 하여 블로그를 직접 만들고 싶은데, 스프링부트 + 그냥JS로 만들어도 괜찮을지 모르겠습니다. (사실 아는 게 그것 뿐입니다..) 혹시 조금 더 편리하고 좋은 스택이 있을까요?
Jaeyeol Lee shared the below article:
자손킴 @jasonkim@hackers.pub
전송 계층은 전송 제어를 하고 애플리케이션별로 패킷을 분류하는 것 까지만 담당한다. 애플리케이션 계층은 패킷을 애플리케이션으로 처리하고 애플리케이션과 사용자를 연결하는 계층이다.
OSI 7계층 모델에서는 세션 계층(L5)과 프레젠테이션 계층(L6)이 별도로 정의되어 있다. 세션 계층은 애플리케이션 간의 세션(연결) 설정, 관리, 종료를 담당하고, 프레젠테이션 계층은 데이터의 형식 변환, 암호화, 압축을 담당한다.
하지만 현대 인터넷의 근간인 TCP/IP 모델에서는 이 두 계층을 애플리케이션 계층과 분리하지 않는다. TCP/IP 모델은 OSI의 L5~L7을 하나의 애플리케이션 계층으로 통합하며 세션 관리나 데이터 표현 방식은 각 애플리케이션 프로토콜이 자체적으로 처리한다. 예를 들어 TCP는 이미 전송 계층에서 연결의 설정과 해제(3-way handshake, 4-way handshake)를 관리하고 TLS는 애플리케이션 프로토콜 수준에서 암호화와 데이터 무결성을 처리한다.
실제로 RFC 3439에는 "Layering considered harmful"이라는 섹션이 있을 정도로 엄격한 계층 분리보다는 실용적인 프로토콜 설계가 중시된다. 이러한 이유로 이 책에서도 L5, L6을 별도로 다루지 않고 애플리케이션 프로토콜로 통합하여 설명한다.
이 책에서는 HTTP, SSL/TLS, DNS, DHCP에 대해서 다룬다.
HTTP(Hypertext Transfer Protocol): 웹 브라우저와 웹 서버 간의 통신을 위한 프로토콜이다. 요청-응답 방식으로 동작하며, 웹 페이지, 이미지, API 데이터 등 다양한 리소스를 전송한다.
SSL/TLS(Secure Sockets Layer/Transport Layer Security): 네트워크 통신을 암호화하여 보안을 제공하는 프로토콜이다. HTTPS는 HTTP에 TLS를 결합한 것으로 웹에서 가장 널리 사용되는 보안 통신 방식이다.
DNS(Domain Name System): 도메인 이름(예: www.example.com)을 IP 주소로 변환하는 시스템이다. 사용자가 기억하기 쉬운 도메인 이름을 사용하여 웹사이트에 접속할 수 있게 해준다.
DHCP(Dynamic Host Configuration Protocol): 네트워크에 연결된 장치에게 IP 주소, 서브넷 마스크, 기본 게이트웨이, DNS 서버 등의 네트워크 설정을 자동으로 할당하는 프로토콜이다.
HTTP는 따로 정리하지 않을 것이고, TLS, DNS, DHCP에 대해서만 정리 할 것이다.
TLS(SSL)은 애플리케이션을 암호화하는 프로토콜이다.
SSL은 1995년 Netscape가 웹 통신 보안을 위해 개발한 프로토콜이다. SSL 2.0이 최초로 공개되었지만 심각한 보안 취약점이 발견되어 1996년 SSL 3.0으로 대체되었다. 이후 IETF(Internet Engineering Task Force)가 SSL을 표준화하는 과정에서 프로토콜 이름이 TLS(Transport Layer Security)로 변경되었다. 1999년 TLS 1.0이 RFC 2246으로 발표되었는데 이는 SSL 3.0을 기반으로 하되 상호 운용성이 없을 정도로 충분한 차이가 있었다.
SSL 3.0은 2014년 POODLE(Padding Oracle On Downgraded Legacy Encryption) 공격 취약점이 발견된 후 2015년 공식적으로 폐기되었다. TLS 1.0과 1.1도 2020년 주요 브라우저들에 의해 지원이 중단되었고 2021년 RFC 8996을 통해 공식 폐기되었다.
현재는 TLS 1.2(2008년 출시)와 TLS 1.3(2018년 출시)이 사용되며 TLS 1.3이 권장된다.
책에서는 TLS 1.2와 RSA를 기반으로 설명하고 있지만 이 포스팅에서는 TLS 1.3과 Ed25519, X25519를 기반으로 정리 할 것이다.
TLS는 스푸핑, 변조, 도청이라는 세 가지 주요 보안 위협을 방지한다.
도청은 통신 당사자가 아닌 제3자가 네트워크를 흐르는 데이터를 몰래 가로채 읽는 행위이다. 공공 와이파이에서 로그인 정보를 훔치거나 네트워크 패킷을 캡처하여 민감한 정보를 탈취하는 것이 대표적인 예다.
암호화는 정해진 규칙(암호화 알고리즘)에 따라 데이터를 변환하는 기술이다. TLS는 대칭키 암호화를 사용하여 통신 내용을 암호문으로 변환한다. 도청자가 암호화된 패킷을 가로채더라도 복호화 키 없이는 원본 데이터를 알 수 없다.
변조(Tampering)는 통신 중인 데이터를 제3자가 중간에서 가로채어 내용을 바꾸는 행위이다. 예를 들어 은행 송금 요청에서 수신자 계좌번호나 금액을 변경하는 중간자 공격(Man-in-the-Middle Attack)이 있다.
해싱은 불규칙한 길이의 데이터에서 정해진 계산(해싱 알고리즘)에 따라 고정된 길이의 데이터(해시값)를 생성하는 기술이다. TLS는 메시지 인증 코드(MAC)를 사용하여 각 메시지에 해시 기반 태그를 붙인다. 수신자는 받은 데이터로 동일한 해시를 계산하고 송신자가 보낸 MAC 태그와 비교한다. 만약 데이터가 조금이라도 변경되었다면 해시값이 완전히 달라지므로 변조를 즉시 탐지할 수 있다.
스푸핑(Spoofing)은 공격자가 다른 서버나 사용자로 위장하여 통신 상대방을 속이는 행위이다. 가짜 은행 웹사이트를 만들어 사용자의 로그인 정보를 탈취하는 피싱 공격이 대표적이다.
디지털 인증서는 인터넷에 있는 다른 단말에 "나는 진짜입니다!"라고 증명하는 파일이다. TLS는 신뢰할 수 있는 인증 기관(CA, Certificate Authority)이 발급한 디지털 인증서를 사용하여 서버의 신원을 검증한다. 클라이언트는 서버가 제시한 인증서가 신뢰할 수 있는 CA에 의해 서명되었는지 인증서의 도메인이 접속하려는 도메인과 일치하는지 확인한다. 이 검증을 통해 가짜 서버에 연결되는 것을 방지한다.
TLS는 암호화 알고리즘, 키 교환 알고리즘, 디지컬 서명 알고리즘, 메시지 인증 알고리즘 4가지 기술을 조합하여 사용한다.
암호화는 평문(원본 데이터)을 암호문(읽을 수 없는 형태)으로 변환하는 과정이며 복호화는 암호문을 다시 평문으로 되돌리는 과정이다. TLS에서 실제 데이터 암호화에는 대칭키(공통키) 암호화 방식을 사용한다.
대칭키 암호화는 암호화와 복호화에 동일한 키를 사용하는 방식이다. AES-GCM이나 ChaCha20-Poly1305 같은 알고리즘이 대표적이며 처리 속도가 빨라 대용량 데이터 암호화에 적합하다.
하지만 대칭키 암호화에는 근본적인 문제가 있다. 통신을 시작하기 전에 양측이 동일한 키를 가지고 있어야 하는데 이 키를 어떻게 안전하게 전달할 것인가? 키를 평문으로 네트워크에 전송하면 도청자에게 탈취당할 수 있다. 키가 탈취되면 해당 키로 암호화된 모든 통신 내용이 노출된다. 이것이 바로 '키 전달 문제'이며 이를 해결하기 위해 키 교환 알고리즘이 필요하다.
공통키 암호 방식을 사용하면 키 전달시 보안 문제를 피할 수 없다. 키 교환 알고리즘은 도청자가 지켜보는 공개 채널을 통해서도 양측이 안전하게 공유 비밀(Shared Secret)을 생성할 수 있게 해주는 기술이다.
이전에는 RSA 키 교환을 사용했다. 클라이언트가 무작위 비밀값을 생성하고 서버의 RSA 공개키로 암호화하여 전송하면 서버가 자신의 개인키로 복호화하는 방식이다. 하지만 이 방식에는 심각한 문제가 있다.
만약 공격자가 암호화된 통신을 모두 저장해두었다가, 나중에 서버의 RSA 개인키가 유출되면 과거의 모든 통신을 복호화할 수 있다. 이를 '전방 비밀성(Forward Secrecy)'이 없다고 한다.
TLS 1.3에서는 RSA 키 교환이 완전히 제거되고, X25519(또는 ECDHE) 같은 임시(Ephemeral) Diffie-Hellman 키 교환만 사용한다. X25519는 Curve25519 타원 곡선을 기반으로 한 ECDH(Elliptic Curve Diffie-Hellman) 키 교환 함수로 Daniel J. Bernstein이 2006년에 설계했다.
X25519의 장점은 다음과 같다:
도청자는 공개키(a × G, b × G)만 볼 수 있는데 여기서 개인키(a, b)를 알아내는 것은 타원 곡선 이산 로그 문제(ECDLP)를 푸는 것으로 현재 기술로는 계산적으로 불가능하다.
앞서 언급했듯이 TLS는 디지털 인증서에 포함된 디지털 서명을 통해 상대방이 제3자가 신뢰 할 수있는 상대인지 여부를 판단한다.
예전에는 RSA가 디지털 서명에 널리 사용되었지만 몇 가지 한계가 있다. 동등한 보안 수준을 위해 훨씬 큰 키 크기가 필요하고(RSA 3072비트 ≈ Ed25519 256비트) 서명 생성 속도가 상대적으로 느리며 구현 시 패딩 오라클 공격 등에 취약할 수 있다.
Ed25519는 이러한 문제를 해결한 현대적인 디지털 서명 알고리즘이다. Edwards 곡선 기반의 EdDSA(Edwards-curve Digital Signature Algorithm) 구현체로, Daniel J. Bernstein 팀이 설계했다. 2023년 FIPS 186-5에 공식 포함되어 미국 연방 정부 시스템에서도 승인된 서명 알고리즘이 되었다.
서명 생성 (서버/발급자 측):
서명 검증 (클라이언트 측):
통신 상대방 인증:
TLS에서 앞서 언급한 디지털 서명 알고리즘은 통신 상대방을 인증하는 것일 뿐 이후 주고받는 애플리케이션 데이터(메시지)를 인증하는 것은 아니다.
디지털 서명은 비대칭키 암호화를 사용하므로 연산 비용이 높다. 매 메시지마다 서명을 생성하고 검증하는 것은 성능상 비효율적이다. 따라서 TLS는 핸드셰이크 과정에서 합의한 대칭키를 사용하는 MAC(Message Authentication Code)으로 각 메시지의 무결성과 인증을 보장한다.
MAC은 메시지와 공유 비밀키를 입력으로 받아 고정 길이의 인증 태그를 생성하는 알고리즘이다. 단순한 해시와 달리, 비밀키가 없으면 올바른 MAC 태그를 생성할 수 없다. 따라서 MAC은 메시지 무결성(변조 여부)과 메시지 인증(발신자 확인)을 동시에 제공한다.
TLS에서는 주로 HMAC(Hash-based MAC)을 사용한다. HMAC은 SHA-256 같은 해시 함수와 비밀키를 결합하여 MAC 태그를 생성한다. TLS 1.3에서는 AEAD(Authenticated Encryption with Associated Data) 모드인 AES-GCM이나 ChaCha20-Poly1305를 사용하는데, 이들은 암호화와 메시지 인증을 동시에 수행한다.
이 과정에서 공격자가 메시지 내용을 조금이라도 바꾸면 해시값이 완전히 달라지므로 올바른 MAC 태그를 생성할 수 없다. 또한 공유 비밀키 없이는 위조된 메시지에 대한 유효한 MAC을 만들 수 없어 메시지의 출처도 검증된다.
Jaeyeol Lee shared the below article:
洪 民憙 (Hong Minhee) @hongminhee@hackers.pub
신고 기능은 Hackers' Pub 커뮤니티의 행동 강령(code of conduct)을 위반하는 콘텐츠나 사용자를 식별하고, 관리자가 적절한 조치를 취할 수 있도록 돕는 시스템입니다.
신고 기능의 궁극적인 목적은 계도와 성장입니다. 무균실처럼 완벽한 사용자만을 남기려는 것이 아니라, 신고를 통해 각자의 행동을 돌아보고 더 나은 커뮤니티 구성원으로 성장할 수 있는 기회를 제공하는 데 있습니다.
추방은 최후의 수단이며, 시스템은 다음과 같은 단계적 접근을 권장합니다:
Hackers' Pub은 ActivityPub 프로토콜 기반의 분산형 소셜 네트워크입니다. 따라서 신고 기능도 다음을 고려하여 설계되었습니다:
| 용어 | 정의 |
|---|---|
| 신고(flag/report) | 행동 강령 위반으로 의심되는 콘텐츠나 사용자를 관리자에게 알리는 행위 |
| 신고자(reporter) | 신고를 제출하는 사용자 |
| 피신고자(reported) | 신고의 대상이 되는 사용자 |
| 신고 대상(target) | 신고된 콘텐츠(게시글, 단문) 또는 사용자 |
| 관리자(moderator) | 신고를 검토하고 조치를 취할 권한이 있는 사용자 |
| 조치(action) | 관리자가 신고에 대해 취하는 결정 (기각, 경고, 검열, 정지 등) |
| 이의 제기(appeal) | 피신고자가 조치에 대해 재검토를 요청하는 행위 |
| 로컬 사용자 | Hackers' Pub에 계정이 있는 사용자 |
| 원격 사용자 | 다른 ActivityPub 인스턴스의 사용자 |
사용자는 다음 유형의 콘텐츠를 개별적으로 신고할 수 있습니다:
특정 사용자의 전반적인 행동 패턴이 문제가 되는 경우, 개별 콘텐츠가 아닌 사용자 자체를 신고할 수 있습니다.
다른 ActivityPub 인스턴스의 콘텐츠와 사용자도 동일하게 신고할 수 있습니다.
Flag 액티비티 전송 (선택적)신고 양식은 간결하면서도 필요한 정보를 수집할 수 있도록 설계됩니다.
신고 사유 (자유 형식 텍스트)
이 콘텐츠/사용자를 신고하는 이유를 설명해 주세요.
구체적인 행동 강령 조항을 알지 못해도 괜찮습니다.
어떤 점이 불편하거나 문제가 된다고 느꼈는지
자유롭게 작성해 주세요.
[ ]
[ ]
[ ]
최소 10자 이상 작성해 주세요.
근거:
추가 콘텐츠 링크 (사용자 신고 시)
관련된 다른 콘텐츠가 있다면 링크를 추가해 주세요. (선택)
[링크 추가 +]
근거: 사용자 신고의 경우, 문제 행동의 패턴을 보여주는 여러 콘텐츠를 함께 제출하면 관리자가 더 정확한 판단을 내릴 수 있습니다.
신고가 제출되면 LLM이 신고 사유를 분석하여 관련된 행동 강령 조항을 식별합니다.
입력 구성
LLM 분석
결과 저장
같은 콘텐츠나 사용자에 대해 여러 신고가 접수될 수 있습니다.
신고자는 자신이 제출한 신고의 상태를 확인할 수 있습니다.
| 상태 | 설명 |
|---|---|
pending |
신고가 접수되어 검토 대기 중 |
reviewing |
관리자가 검토 중 |
resolved |
처리 완료 (조치됨) |
dismissed |
기각됨 (위반 아님) |
관리자는 다음 정보를 종합적으로 검토합니다:
관리자는 다음 조치 중 하나를 선택합니다:
| 조치 | 설명 | 적용 기준 |
|---|---|---|
| 기각 | 위반이 아니라고 판단 | 행동 강령 위반 사실이 없는 경우 |
| 경고 | 경고 메시지 발송 | 경미한 위반, 초범인 경우 |
| 콘텐츠 검열 | 해당 콘텐츠 숨김 처리 | 콘텐츠 자체가 문제인 경우 |
| 일시 정지 | 일정 기간 계정 정지 | 반복 위반 또는 중간 수준의 위반 |
| 영구 정지 | 계정 영구 정지 | 심각한 위반 또는 지속적 악의적 행동 |
관리자가 조치를 취할 때 다음을 기록해야 합니다:
위반 조항 (최종 확정):
[행동 강령 내 관련 조항 선택/입력]
조치 사유:
[관리자의 판단 근거를 상세히 기술]
피신고자에게 전달할 메시지:
[피신고자가 받을 통보 내용]
(일시 정지의 경우) 정지 기간:
[시작일] – [종료일]
근거:
피신고자는 자신이 신고되었다는 사실과 사유를 알림으로 받습니다.
즉시 통보하지 않는 경우:
통보하는 경우:
경고/제재 시:
귀하의 [콘텐츠/계정]에 대해 신고가 접수되어 검토한 결과,
행동 강령 위반으로 판단되어 다음과 같은 조치가 취해졌습니다.
위반 내용:
[행동 강령의 관련 조항]
대상 콘텐츠:
[해당되는 경우 콘텐츠 링크]
조치:
[경고 / 콘텐츠 검열 / N일 정지 / 영구 정지]
관리자 메시지:
[관리자가 작성한 설명]
이 조치에 대해 이의가 있으시면 아래 버튼을 통해
이의 제기를 하실 수 있습니다.
[이의 제기하기]
기각 통보 시 (선택적):
귀하의 [콘텐츠/계정]에 대해 신고가 접수되었으나,
검토 결과 행동 강령 위반에 해당하지 않는다고 판단되었습니다.
다만, 일부 커뮤니티 구성원이 불편함을 느꼈을 수 있으므로
참고해 주시면 감사하겠습니다.
관련 내용:
[간략한 설명]
| 정보 | 확인 가능 여부 |
|---|---|
| 신고된 사실 | 가능 |
| 위반으로 지적된 행동 강령 조항 | 가능 |
| 대상 콘텐츠 | 가능 |
| 조치 내용 및 기간 | 가능 |
| 관리자의 판단 사유 | 가능 |
| 신고자가 누구인지 | 불가능 |
| 신고자가 작성한 원본 사유 | 불가능 |
| 신고 건수 | 불가능 |
근거: 피신고자에게 개선에 필요한 정보는 모두 제공하되, 신고자를 특정할 수 있는 정보는 철저히 보호합니다.
이의 제기 사유:
[왜 이 조치가 부당하다고 생각하시는지 설명해 주세요]
추가 맥락 또는 증거:
[조치 결정 시 고려되지 않았다고 생각되는
맥락이나 정보가 있다면 제공해 주세요]
[제출]
피신고자에게:
귀하의 이의 제기를 검토한 결과를 알려드립니다.
결정: [이의 기각 / 조치 완화 / 조치 철회]
판단 사유:
[관리자의 검토 결과 설명]
(해당 시) 변경된 조치:
[새로운 조치 내용]
원 신고자에게:
귀하가 신고하신 건에 대해 피신고자로부터
이의 제기가 있어 재검토가 진행되었습니다.
재검토 결과: [원 조치 유지 / 조치 변경]
(조치가 변경된 경우)
변경 사유에 대한 간략한 설명:
[설명]
Delete 액티비티가 전송될 수 있습니다검열 콘텐츠 표시
이 콘텐츠는 행동 강령 위반으로 검열되었습니다.
[원문 보기] (클릭 시 경고와 함께 표시)
Flag 액티비티로 통보Flag 액티비티로 통보| 패널티 | 이력 보존 기간 | 비고 |
|---|---|---|
| 경고 | 1년 | 1년간 추가 위반 없으면 이력에서 제외 |
| 콘텐츠 검열 | 무기한 | 콘텐츠 존재하는 한 유지 |
| 일시 정지 | 무기한 | 기록은 유지, 판단 시 경과 시간 고려 |
| 영구 정지 | 무기한 | - |
Hackers' Pub은 ActivityPub 프로토콜을 사용하는 분산형 네트워크의 일부입니다. 신고 기능도 이 환경에서 원활히 작동해야 합니다.
Flag 액티비티 ActivityPub 명세에는 Flag 액티비티가 정의되어 있으며, 이를 통해 신고를
연합 네트워크에 전파할 수 있습니다.
Flag 액티비티 구조:
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Flag",
"actor": "https://hackerspub.example/users/moderator",
"object": [
"https://remote.example/users/reported_user",
"https://remote.example/posts/problematic_post"
],
"content": "Violation of Code of Conduct: harassment"
}
Hackers' Pub 내 조치:
원격 서버 통보 (선택적):
Flag 액티비티를 원격 서버에 전송Flag 처리 다른 서버에서 Hackers' Pub으로 Flag 액티비티가 전송된 경우:
Flag 액티비티 수신 및 파싱외부 신고 표시:
[외부 신고] remote.example에서 접수됨
대상: @localuser의 콘텐츠
사유: "Violation of our community guidelines"
* 이 신고는 외부 서버에서 접수되었습니다.
자체 행동 강령에 따라 판단해 주세요.
Mastodon은 가장 널리 사용되는 ActivityPub 구현체입니다. Mastodon과의 호환성을 위해 다음을 고려합니다:
Flag 액티비티 형식 지원| 알림 유형 | 수신자 | 내용 |
|---|---|---|
flag_received |
관리자 | 새 신고 접수됨 |
flag_resolved |
신고자 | 신고 처리 완료됨 |
action_taken |
피신고자 | 조치가 취해짐 |
appeal_received |
관리자 | 이의 제기 접수됨 |
appeal_resolved |
피신고자 | 이의 제기 처리 완료됨 |
appeal_result |
신고자 | 이의 제기로 인한 변경 알림 |
suspension_ending |
피신고자 | 정지 해제 임박 알림 |
| 역할 | 접근 가능 정보 |
|---|---|
| 일반 사용자 | 자신의 신고 내역만 |
| 피신고자 | 자신에 대한 조치 및 사유 (신고자 정보 제외) |
| 관리자 | 모든 신고 정보 (신고자 정보 포함) |
신고 시점의 콘텐츠를 스냅샷으로 저장하는 이유:
관리자 대시보드는 신고 관리의 중심 허브입니다.
┌─────────────────────────────────────────────────────────┐
│ 신고 관리 [통계 보기] │
├─────────────────────────────────────────────────────────┤
│ 필터: [전체 ▼] [대기 중 ▼] [최신순 ▼] 검색: [____]│
├─────────────────────────────────────────────────────────┤
│ │
│ ⚠️ 높은 우선순위 (신고 5건 이상) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 🔴 @user123의 콘텐츠 (신고 7건) │ │
│ │ "혐오 발언", "차별적 표현" 외 5건 │ │
│ │ 최초 신고: 2시간 전 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 일반 신고 │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 🟡 @remote@other.server 사용자 (신고 2건) │ │
│ │ "스팸 행위" │ │
│ │ 최초 신고: 5시간 전 │ │
│ └─────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 🟢 @newuser의 댓글 (신고 1건) │ │
│ │ "부적절한 언어 사용" │ │
│ │ 신고: 1일 전 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│ 신고 상세 - 케이스 #12345 [← 목록] │
├─────────────────────────────────────────────────────────┤
│ │
│ 📋 기본 정보 │
│ ──────────────────────────────────────── │
│ 대상: @user123의 콘텐츠 │
│ 유형: 단문 (note) │
│ 신고 건수: 7건 │
│ 상태: 대기 중 │
│ │
│ 📝 신고된 콘텐츠 │
│ ──────────────────────────────────────── │
│ ┌─────────────────────────────────────────────────┐ │
│ │ [콘텐츠 원문 표시] │ │
│ │ 작성일: 2024-12-01 14:30 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 🔍 신고 사유 (7건) │
│ ──────────────────────────────────────── │
│ 1. "명백한 혐오 발언입니다" - 신고자A, 2시간 전 │
│ 2. "특정 집단을 비하하는 표현" - 신고자B, 3시간 전 │
│ 3. "불쾌한 차별적 언어" - 신고자C, 4시간 전 │
│ ... (더 보기) │
│ │
│ 🤖 LLM 분석 결과 │
│ ──────────────────────────────────────── │
│ 관련 행동 강령 조항: │
│ - 차별 금지 (신뢰도: 95%) │
│ - 존중하는 언어 사용 (신뢰도: 88%) │
│ │
│ 📊 피신고자 이력 │
│ ──────────────────────────────────────── │
│ - 가입일: 2024-06-15 │
│ - 이전 경고: 1회 (2024-09-20) │
│ - 이전 정지: 없음 │
│ │
│ ⚡ 조치 │
│ ──────────────────────────────────────── │
│ [기각] [경고] [콘텐츠 검열] [일시 정지] [영구 정지] │
│ │
└─────────────────────────────────────────────────────────┘
기간 선택 드롭다운으로 조회 범위를 설정합니다 (예: 최근 30일).
| 항목 | 값 |
|---|---|
| 총 신고 건수 | 127건 |
| 처리 완료 | 98건 (77%) |
| 평균 처리 시간 | 4.2시간 |
| 조치 | 건수 | 비율 |
|---|---|---|
| 기각 | 45건 | 46% |
| 경고 | 38건 | 39% |
| 콘텐츠 검열 | 10건 | 10% |
| 일시 정지 | 4건 | 4% |
| 영구 정지 | 1건 | 1% |
| 순위 | 유형 | 건수 |
|---|---|---|
| 1 | 스팸/광고 | 32건 |
| 2 | 혐오 발언 | 24건 |
| 3 | 괴롭힘 | 18건 |
| 4 | 부적절한 콘텐츠 | 12건 |
| 5 | 허위 정보 | 8건 |
주의
자동화 기능은 오탐의 위험이 있으므로 신중하게 도입해야 합니다.
| 한국어 | 영어 | 설명 |
|---|---|---|
| 신고 | flag/report | 위반 의심 콘텐츠/사용자를 알림 |
| 행동 강령 | code of conduct | 커뮤니티 규칙 |
| 관리자 | moderator | 신고 처리 권한자 |
| 검열 | censorship | 콘텐츠 숨김 처리 |
| 정지 | suspension | 계정 활동 제한 |
| 이의 제기 | appeal | 조치에 대한 재검토 요청 |
| 연합 | federation | 분산 네트워크 간 연결 |
| 콘텐츠 | post | 게시글과 단문을 통칭 |
| 게시글 | article | 장문의 블로그 형식 글 |
| 단문 | note | 짧은 마이크로블로그 형식 글 |
| 타임라인 | timeline | 콘텐츠 피드 |
| 팔로 | follow | 다른 사용자 구독 |
| 팔로워 | follower | 나를 구독하는 사용자 |
| 차단 | block | 특정 사용자 접근 제한 |
| 반응 | react | 콘텐츠에 이모지로 반응 |
| 연합우주 | fediverse | ActivityPub 기반 분산 소셜 네트워크 |
| 인스턴스 | instance | 연합우주의 개별 서버 |
이 문서는 Hackers' Pub 커뮤니티의 의견을 수렴하여 지속적으로 개선됩니다.
Calling all #fediverse developers for help: I'm currently trying to implement a #reporting (#flag) feature for Hackers' Pub, an #ActivityPub-enabled community for software engineers. Is there a formal specification for how cross-instance reporting should work in ActivityPub? Or, is there any well-documented material that explains how the major implementations handle it?
Jaeyeol Lee shared the below article:
자손킴 @jasonkim@hackers.pub
이번 글에서는 지난글에 이어서 Claude가 도구를 사용하는 구체적인 방법을 알아본다. Claude가 사용할 수 있는 도구들의 목록은 Tools 섹션에 포함되어 있다. Tools 섹션에 대해서는 이전 글을 참고한다.
Tool Use는 Claude가 외부 도구(함수)를 호출하여 실제 작업을 수행할 수 있게 하는 메커니즘이다. Claude는 텍스트 생성만으로는 수행할 수 없는 작업들, 예를 들어 파일 읽기, 명령어 실행, 웹 검색 등을 도구를 통해 수행한다.
Claude에게 사용 가능한 도구들의 스키마를 알려주면 Claude는 사용자의 요청을 분석하여 적절한 도구를 선택하고 필요한 파라미터와 함께 도구 사용을 요청한다. 에이전트(클라이언트)는 이 요청을 받아 실제로 도구를 실행하고 그 결과를 다시 Claude에게 전달한다.
Claude가 도구를 사용하려면 먼저 어떤 도구가 있는지 알아야 한다. 에이전트는 API 요청의 tools 배열에 사용 가능한 도구들을 정의한다. 각 도구는 이름, 설명, 그리고 입력 스키마를 포함한다.
{
"name": "Bash",
"description": "Executes a given bash command in a persistent shell session with optional timeout, ensuring proper handling and security measures.\n\nIMPORTANT: This tool is for terminal operations like git, npm, docker, etc...",
"input_schema": {
"type": "object",
"properties": {
"command": {
"type": "string",
"description": "The command to execute"
},
"timeout": {
"type": "number",
"description": "Optional timeout in milliseconds (max 600000)"
},
"description": {
"type": "string",
"description": "Clear, concise description of what this command does in 5-10 words, in active voice."
}
},
"required": ["command"],
"additionalProperties": false,
"$schema": "http://json-schema.org/draft-07/schema#"
}
}
{
"name": "Glob",
"description": "- Fast file pattern matching tool that works with any codebase size\n- Supports glob patterns like \"**/*.js\" or \"src/**/*.ts\"\n- Returns matching file paths sorted by modification time\n- Use this tool when you need to find files by name patterns",
"input_schema": {
"type": "object",
"properties": {
"pattern": {
"type": "string",
"description": "The glob pattern to match files against"
},
"path": {
"type": "string",
"description": "The directory to search in. If not specified, the current working directory will be used."
}
},
"required": ["pattern"],
"additionalProperties": false,
"$schema": "http://json-schema.org/draft-07/schema#"
}
}
도구 정의에서 description이 중요하다. Claude는 이 설명을 읽고 어떤 상황에서 해당 도구를 사용해야 하는지 판단한다. input_schema는 JSON Schema 형식으로 Claude가 도구를 호출할 때 어떤 파라미터를 어떤 형식으로 전달해야 하는지 정의한다.
Claude가 도구를 선택하는 과정은 Messages API의 대화 흐름 속에서 이루어진다. 실제 예시를 통해 살펴보자.
사용자가 "이 NestJS 프로젝트에서 entity 구조를 탐색해주세요"라고 요청하면 에이전트는 다음과 같은 메시지를 API에 전송한다:
{
"role": "user",
"content": [
{
"type": "text",
"text": "이 NestJS TypeScript 프로젝트에서 entity 구조를 탐색해주세요..."
}
]
}
Claude는 사용자의 요청을 분석하고 작업 수행에 필요한 도구들을 선택하여 tool_use 블록으로 응답한다:
{
"role": "assistant",
"content": [
{
"type": "text",
"text": "이 NestJS 프로젝트의 entity 구조를 철저하게 탐색하겠습니다."
},
{
"type": "tool_use",
"id": "toolu_01ABC123XYZ",
"name": "Glob",
"input": {
"pattern": "**/*.entity.ts"
}
},
{
"type": "tool_use",
"id": "toolu_01DEF456UVW",
"name": "Bash",
"input": {
"command": "find /workspace/my-nestjs-project/src -type f -name \"*.ts\" | grep -E \"(entity|entities)\" | head -20",
"description": "Find entity files in src directory"
}
}
]
}
여기서 주목할 점이 있다. Claude는 한 번의 응답에서 여러 도구를 동시에 요청할 수 있다. 위 예시에서는 Glob과 Bash 두 도구를 병렬로 요청했다. 각 도구 요청에는 고유한 id가 부여되어 나중에 결과를 매핑할 때 사용된다.
Claude가 도구 사용을 요청하면 API 응답의 stop_reason이 "tool_use"로 설정된다:
{
"id": "msg_01XYZ789ABC",
"type": "message",
"role": "assistant",
"model": "claude-haiku-4-5-20251001",
"content": [...],
"stop_reason": "tool_use",
"usage": {
"input_tokens": 714,
"output_tokens": 314
}
}
이 stop_reason은 에이전트에게 "응답이 끝난 것이 아니라 도구 실행이 필요하다"는 신호를 보낸다.
에이전트(클라이언트)가 stop_reason: "tool_use" 응답을 받으면 다음 단계를 수행해야 한다:
도구 요청 파싱: 응답의 content 배열에서 type: "tool_use" 블록들을 추출한다.
도구 실행: 각 도구 요청에 대해 실제 도구를 실행한다. 예를 들어:
Bash 도구 → 시스템에서 실제 bash 명령어 실행Glob 도구 → 파일 시스템에서 패턴 매칭 수행Read 도구 → 파일 내용 읽기결과 수집: 각 도구의 실행 결과를 수집하고 tool_use_id와 함께 결과를 구성한다.
모델에 결과 전달: 수집한 결과를 tool_result 형식으로 모델에 다시 전송한다.
이 과정에서 에이전트는 도구 실행의 성공/실패 여부, 타임아웃 처리, 보안 검증 등을 담당한다. Claude는 도구의 스키마와 용도만 알 뿐 실제 실행은 에이전트의 몫이다.
에이전트가 도구를 실행한 후에는 그 결과를 tool_result 형식으로 모델에 전달한다. 이 결과는 user role의 메시지로 전송된다.
{
"role": "user",
"content": [
{
"tool_use_id": "toolu_01DEF456UVW",
"type": "tool_result",
"content": "/workspace/my-nestjs-project/src/modules/chat/entities/dm-unlock.entity.ts\n/workspace/my-nestjs-project/src/modules/agora/entities/call-session.entity.ts\n/workspace/my-nestjs-project/src/modules/user/entities/user.entity.ts\n/workspace/my-nestjs-project/src/modules/user/entities/user-profile.entity.ts\n/workspace/my-nestjs-project/src/modules/item/entities/item.entity.ts\n...",
"is_error": false
},
{
"tool_use_id": "toolu_01ABC123XYZ",
"type": "tool_result",
"content": "/workspace/my-nestjs-project/src/modules/agora/entities/agora-event-log.entity.ts\n/workspace/my-nestjs-project/src/modules/agora/entities/call-participant.entity.ts\n/workspace/my-nestjs-project/src/modules/item/entities/item.entity.ts\n...",
"cache_control": {
"type": "ephemeral"
}
}
]
}
각 tool_result의 핵심 필드는 다음과 같다:
| 필드 | 설명 |
|---|---|
tool_use_id |
Claude가 요청한 도구의 고유 ID. 어떤 요청에 대한 결과인지 매핑 |
type |
항상 "tool_result" |
content |
도구 실행의 실제 결과 (문자열) |
is_error |
도구 실행 실패 시 true |
cache_control |
(선택) 프롬프트 캐싱을 위한 제어 옵션 |
tool_result를 받은 Claude는 결과를 분석하고 추가 도구가 필요하면 다시 tool_use를 요청한다. 충분한 정보가 모이면 최종 응답을 생성한다. 이 과정이 반복되면서 복잡한 작업도 단계별로 수행할 수 있다:
User → Claude: "entity 구조를 탐색해주세요"
Claude → Agent: tool_use (Glob, Bash)
Agent → Claude: tool_result (파일 목록)
Claude → Agent: tool_use (Read - 여러 파일)
Agent → Claude: tool_result (파일 내용들)
Claude → User: 최종 분석 결과
실제 예시에서 Claude는 먼저 Glob과 Bash로 entity 파일 목록을 찾고 그 결과를 받은 후 Read 도구로 개별 파일들을 읽어 분석했다:
{
"type": "text",
"text": "좋습니다. 이제 주요 entity 파일들을 읽겠습니다."
},
{
"type": "tool_use",
"id": "toolu_01GHI789RST",
"name": "Read",
"input": {
"file_path": "/workspace/my-nestjs-project/src/modules/user/entities/user.entity.ts"
}
},
{
"type": "tool_use",
"id": "toolu_01JKL012MNO",
"name": "Read",
"input": {
"file_path": "/workspace/my-nestjs-project/src/modules/user/entities/user-profile.entity.ts"
}
}
Claude Code와 같은 에이전트는 모델에 사용할 수 있는 도구를 알려주어 도구를 능동적으로 사용하게 만듦으로써 유저의 실행환경과 상호 협력하여 도구를 실행한다. 유저에게 질문을 하는 AskUserQuestion도 도구이고 심지어 계획 모드를 빠져나가는 ExitPlanMode도 도구다.
MCP(Model Context Protocol) 서버가 제공하는 기능들도 결국 도구로 노출되며 Subagent 호출도 도구를 통해 이루어진다. Skills도 마찬가지다. 결국 Claude Code의 거의 모든 확장 기능은 Tool Use라는 하나의 메커니즘 위에서 동작한다.
이 구조를 이해하면 Claude Code가 어떻게 파일을 읽고, 코드를 실행하고, 웹을 검색하는지 명확해진다. 그리고 새로운 도구를 추가하거나 MCP 서버를 연동할 때도 같은 패턴이 적용된다는 것을 알 수 있다.
Jaeyeol Lee shared the below article:
자손킴 @jasonkim@hackers.pub
Claude API의 Request는 크게 4가지 분류를 가지고 있다.
각각은 다음과 같은 역할을 한다.
System Messages는 Claude에게 역할, 성격, 제약사항 등을 지시하는 최상위 설정이다. 배열 형태로 여러 개의 시스템 메시지를 전달할 수 있다.
"system": [
{
"type": "text",
"text": "You are Claude Code, Anthropic's official CLI for Claude.",
"cache_control": {
"type": "ephemeral"
}
},
{
"type": "text",
"text": "You are an interactive CLI tool that helps users with software engineering tasks...",
"cache_control": {
"type": "ephemeral"
}
}
]
System Messages에는 다음과 같은 내용이 포함된다:
cache_control을 통한 캐싱 설정Messages는 user와 assistant 역할이 번갈아가며 주고받은 대화를 누적하는 배열이다. assistant 메시지는 반드시 모델의 실제 응답일 필요가 없다. 이를 활요해 API 호출 시 assistant 메시지를 미리 작성해서 전달하면, Claude는 그 내용 이후부터 이어서 응답한다. 이를 Prefill 기법이라 한다.
이 대화 기록을 통해 Claude는 맥락을 유지하며 응답한다.
"messages": [
{
"role": "user",
"content": [...]
},
{
"role": "assistant",
"content": [...]
},
{
"role": "user",
"content": [...]
}
]
User의 content는 주로 두 가지 type으로 구성된다:
1. text - 사용자의 일반 메시지나 시스템 리마인더
{
"role": "user",
"content": [
{
"type": "text",
"text": "선물을 주고받는 기능을 위한 entity를 설계하라."
}
]
}
2. tool_result - Tool 실행 결과 반환
{
"role": "user",
"content": [
{
"tool_use_id": "toolu_01Qj7gnFLKWBNjg",
"type": "tool_result",
"content": [
{
"type": "text",
"text": "## Entity 구조 탐색 보고서\n\n철저한 탐색을 통해..."
}
]
}
]
}
Assistant의 content는 주로 세 가지 type으로 구성된다:
1. text - Claude의 응답 메시지
{
"type": "text",
"text": "선물 주고받기 기능을 위한 entity 설계를 시작하겠습니다."
}
2. thinking - Extended Thinking 기능 활성화 시 사고 과정 (signature로 검증)
{
"type": "thinking",
"thinking": "사용자가 선물을 주고받는 기능을 위한 entity 설계를 요청했습니다...",
"signature": "EqskYIChgCKknyFYp5cu1zhVOp7kFTJb..."
}
3. tool_use - Tool 호출 요청
{
"type": "tool_use",
"id": "toolu_01Qj7gn6vLKCNjg",
"name": "Task",
"input": {
"subagent_type": "Explore",
"prompt": "이 NestJS TypeScript 프로젝트에서 entity 구조를 탐색해주세요...",
"description": "Entity 구조 탐색"
}
}
Tool 사용 흐름은 다음과 같이 진행된다:
tool_use로 Tool 호출 요청tool_result로 실행 결과 반환text 응답 또는 추가 tool_use이 과정에서 어떤 Tool을 사용할 수 있는지는 tools 배열이 정의한다.
Tools는 Claude가 사용할 수 있는 도구들을 정의하는 배열이다. 각 Tool은 name, description, input_schema 세 가지 필드로 구성된다.
"tools": [
{
"name": "ToolName",
"description": "Tool에 대한 설명...",
"input_schema": {
"type": "object",
"properties": {...},
"required": [...],
"additionalProperties": false,
"$schema": "http://json-schema.org/draft-07/schema#"
}
}
]
| 필드 | 설명 |
|---|---|
name |
Tool의 고유 식별자. Claude가 tool_use에서 이 이름으로 호출 |
description |
Tool의 용도, 사용법, 주의사항 등을 상세히 기술. Claude가 어떤 Tool을 선택할지 판단하는 근거 |
input_schema |
JSON Schema 형식으로 입력 파라미터 정의 |
input_schema는 JSON Schema draft-07 스펙을 따르며, Tool 호출 시 필요한 파라미터를 정의한다.
"input_schema": {
"type": "object",
"properties": {
"pattern": {
"type": "string",
"description": "The regular expression pattern to search for"
},
"path": {
"type": "string",
"description": "File or directory to search in. Defaults to current working directory."
},
"output_mode": {
"type": "string",
"enum": ["content", "files_with_matches", "count"],
"description": "Output mode: 'content' shows matching lines, 'files_with_matches' shows file paths..."
},
"-i": {
"type": "boolean",
"description": "Case insensitive search"
},
"head_limit": {
"type": "number",
"description": "Limit output to first N lines/entries"
}
},
"required": ["pattern"],
"additionalProperties": false,
"$schema": "http://json-schema.org/draft-07/schema#"
}
각 파라미터는 다음 필드들로 정의된다:
| 필드 | 설명 |
|---|---|
type |
데이터 타입 (string, number, boolean, array, object 등) |
description |
파라미터의 용도와 사용법 설명 |
enum |
(선택) 허용되는 값의 목록. 이 중 하나만 선택 가능 |
default |
(선택) 기본값 |
| 필드 | 설명 |
|---|---|
type |
항상 "object" |
properties |
파라미터 정의 객체 |
required |
필수 파라미터 이름 배열. 여기 포함되지 않은 파라미터는 선택적 |
additionalProperties |
false면 정의되지 않은 파라미터 전달 불가 |
$schema |
JSON Schema 버전 명시 |
{
"name": "Grep",
"description": "A powerful search tool built on ripgrep\n\n Usage:\n - ALWAYS use Grep for search tasks...",
"input_schema": {
"type": "object",
"properties": {
"pattern": {
"type": "string",
"description": "The regular expression pattern to search for in file contents"
},
"path": {
"type": "string",
"description": "File or directory to search in (rg PATH). Defaults to current working directory."
},
"glob": {
"type": "string",
"description": "Glob pattern to filter files (e.g. \"*.js\", \"*.{ts,tsx}\")"
},
"output_mode": {
"type": "string",
"enum": ["content", "files_with_matches", "count"],
"description": "Output mode. Defaults to 'files_with_matches'."
},
"-A": {
"type": "number",
"description": "Number of lines to show after each match"
},
"-B": {
"type": "number",
"description": "Number of lines to show before each match"
},
"-i": {
"type": "boolean",
"description": "Case insensitive search"
},
"multiline": {
"type": "boolean",
"description": "Enable multiline mode. Default: false."
}
},
"required": ["pattern"],
"additionalProperties": false,
"$schema": "http://json-schema.org/draft-07/schema#"
}
}
이 Tool을 Claude가 호출할 때의 tool_use:
{
"type": "tool_use",
"id": "toolu_01ABC123",
"name": "Grep",
"input": {
"pattern": "class.*Entity",
"path": "src/modules",
"glob": "*.ts",
"output_mode": "content",
"-i": true
}
}
required에 pattern만 있으므로 나머지는 선택적이다. Claude는 input_schema의 description을 참고하여 적절한 파라미터를 선택한다.
마지막으로 모델 선택과 각종 설정 옵션들이다:
{
"model": "claude-opus-4-5-20251101",
"max_tokens": 32000,
"thinking": {
"budget_tokens": 31999,
"type": "enabled"
},
"stream": true,
"metadata": {
"user_id": "user_2f2ce5dbb94ac27c8da0d0b28dddf815fc82be54e0..."
}
}
| 옵션 | 설명 |
|---|---|
model |
사용할 Claude 모델 (claude-opus-4-5, claude-sonnet-4-5 등) |
max_tokens |
최대 출력 토큰 수 |
thinking |
Extended Thinking 설정 (budget_tokens로 사고 토큰 예산 설정) |
stream |
스트리밍 응답 여부 |
metadata |
사용자 ID 등 메타데이터 |
지금까지 Claude API Request Body의 4가지 핵심 구성 요소를 살펴보았다:
이 구조를 알면 Claude가 주고받은 메시지를 어떻게 관리하는지, 도구를 어떻게 사용하는지 이해하고 API를 더 효과적으로 활용할 수 있다.
LLMs are good at coding. They can code faster than human. But they don't have ability to maintain their shit. Don't worry developers, there will be tons of shit codes we need to clean up in future. Surely we won't loose our job. Actually there will be more works than ever.
https://github.com/yamadashy/repomix/releases/tag/v1.10.0
Repomix가 소스코드를 압축해서 LLM 친화적인 텍스트를 뽑아주는 CLI도구인데, 이제 그걸 넘어서 Claude Code Skill도 뽑아주는 기능이 추가되었다 .....
식탁보 1.14.0에서 오랫만에 업데이트를 진행하면서, 생성형 AI의 도움을 받아 적극적인 현대화를 달성하고 있습니다.
InnoSetup 대신 Velopack을 사용한 간소화된 사용자 인스톨러 경험 구현
MSBUILD 프로젝트 대신 .NET SDK로 .NET Framework 프로젝트 마이그레이션 (추후 완전히 .NET 10과 Avalonia로도 전환할 수 있게 함)
TableCloth 프로젝트의 경우 .NET 8/9에서 .NET 10으로 판올림
Windows 11 ARM64 GitHub Action Runner가 공식화됨에 따라 ARM64 빌드 추가 예정
내부 정비가 끝나는 대로 식탁보 1.15.0 버전을 출시하도록 하겠습니다. 또한 생성형 AI 코드 어시스턴트의 도움을 적극 받아 1인 개발에서 오는 한계를 극복해보려 합니다.
최신 소스 커밋 목록은 https://github.com/yourtablecloth/TableCloth/commits/main/ 에서 확인하실 수 있습니다.
At last, I finished reading Chapter 7 of "Theorem Proving in Lean 4" in Korean by explaining how to use three tactics: cases, induction, and injection. https://youtu.be/N-ELdwO-vN4?si=kfemVPbbNP-Gf0m8
12月 6日 서울에서 開催되는 liftIO 2025에서 〈Optique: TypeScript에서 CLI 파서 컴비네이터를 만들어 보았다〉(假題)라는 主題로 發表를 하게 되었습니다. 아직 liftIO 2025 티켓은 팔고 있으니, 函數型 프로그래밍에 關心 있으신 분들의 많은 參與 바랍니다!
오늘 liftIO 2025에서 發表한 〈Optique: TypeScript의 타입 推論으로 CLI 有效性 檢査를 代替하기〉의 發表 資料를 共有합니다! 들어주신 모든 분들께 感謝 드립니다.
SolidJS로 앱 만들다가 아이콘셋이 필요해져서 패키지를 뒤져보는데, 마이너 생태계답게 마지막 업데이트가 삼사년 전인 패키지들만 나온다. 아이콘셋에 업데이트가 필요 없긴 하지. 그래도 최근에 업데이트 된 패키지가 걸리적거리는게 없을 것 같달까. 그러다 활발히 업데이트 중인 unplugin-icons를 찾았다. 이것은 SolidJS용 패키지가 아니었다. 아이콘셋도 아니었다. 거의 모든 아이콘셋을 거의 모든 프레임워크에서 사용할 수 있게 해주는 도구다. 이런 문물이 있었다니. 누가 만들었나 함 보자. 제작자는 Anthony Fu... 아아 또 그인가. 오늘도 비 React 웹 생태계엔 Anthony Fu의 은혜가 넘친다.
```
반대로, 중요한 아키텍처 결정을 내리거나 복잡한 이해관계자 요구사항을 해석하거나 새로운 알고리즘을 설계해야 하는 작업은 AI 지원을 받는 인간 주도 개발에 더 적합합니다. 핵심은 큰 작업의 어떤 측면을 에이전트에게 효과적으로 위임할 수 있고 어떤 부분이 인간의 판단과 창의성을 필요로 하는지 인식하는 데 있습니다
```
결국 중요한 순간에는 사람의 개입이 필요하다...
아카이브 쌓아만두고 후기를 안 적었네.... 일 생각만 하느라 후기 작성도 미루게 되는 느낌이 없지는 않지만, 암튼 후기를 남기자면......
내가 딱 바이브코딩에 대해 가지고 있던 생각들이 책을 통해서 재확인받는 것 같은 느낌이 든다. 바이브코딩이 확실히 개발 진입장벽을 낮춰주기도 하고, 생산성에 부스트를 달아주는 편이라고는 하지만, 최종적으로는 사람이 책임져야한다는 입장이긴 했었다.
뭔가를 빨리 만들고, 그럴싸한 제품을 만들었다고 치자. 근데, 그런 제품을 누가 써? 그렇게 만들어서 어떤 비즈니스 임팩트가 있어? 품질은 누가 책임져? 라는 생각이 안들 수가 없는데 이런 관점에 대해서도 균형적인 시각을 주고 있다.
거듭해서 무작정 그냥 갖다쓰지말고 어떻게 동작하는지 명확히 이해하고 갖다쓰라고하는데 어떻게 보면 라이브러리/프레임워크 갖다 쓰는 것의 좀 더 고차원레벨의 관점에서 접근하는 것이라고도 볼 수 있을 것 같다.
닷넷은 8.0이후로 Microsoft만이 아니라, Canonical, RedHat 등 각 리눅스 배포판 관리자들이 Microsoft를 대신하여 본인들의 배포판 OS에서 잘 작동할 수 있도록 검증 과정을 거쳐 독자적으로 빌드하여 패키징을 하고 있습니다.
그 덕분에 매우 유의미한 발전이 하나 있었는데, IBM 메인프레임 (s390x)과 IBM PowerPC (ppc64el)에서도 우분투 리눅스를 사용하면 이제 닷넷 10을 아주 손쉽게 apt install dotnet-sdk-10.0 명령어 하나로 바로 설치해서 쓸 수 있게 됩니다.
https://forum.dotnetdev.kr/t/ibm-s390x-ibm-powerpc-ppc64el-10/14096
We've got some updates on TypeScript 7! The new native port
- can type-check any project
- supports --build and --incremental
- has rich editor features implemented
- is still 10x faster
and is ready for you to try today!
https://devblogs.microsoft.com/typescript/progress-on-typescript-7-december-2025/
JSConf JP 2025に参加しました!登壇とブース出展のレポート - Money Forward Developers Blog
https://moneyforward-dev.jp/entry/2025/12/01/jsconf-2025-participation-report
읽을책 <면접의 질문들> 저자 김형석
자기계발 | 노르웨이숲 | 200p
[7일차] 완독
면접관도 사람이다. 해결해야 할 일이 있고, 그 일을 하는 데 필요한 사람을 찾고 있을 뿐이다. 누군가를 평가하고 자신의 위치를 과시하려는 것이 아니라 고민하고 있는 것들을 해결 해 줄 수 있는 사람을 열심히 찾고 있다. 지원자가 회사에 들어 가고 싶은 마음 못지않게 면접관도 ‘같이 일하고 싶은 사람’을 찾고 싶다는 기대를 갖고 면접장에 들어온다. 면접은 그런 두 사람이 만나는 곳이다. 조금은 가볍게, 조금은 더 즐겁게 면접에 임하자.
면접에서 느끼는 부담감을 조금 내려놓아야겠습니다.
Draft API to create Collections (#37049) by oneiros has been merged! https://github.com/mastodon/mastodon/commit/f896bbac3b36632f943aaf073bfa050df680af99
I'd love to go to
@fosdem 2026, but Brussels is just too far from Seoul… and the plane tickets are so expensive. 😭
Update: I just submitted a talk proposal to the Social Web Devroom at
@fosdem 2026—Fedify: Type-safe ActivityPub for TypeScript. Guess I'm flying to Brussels after all. 😅
오이카페 모바일 앱이 출시되었어요! 베타 테스트에 참여해주신 여러분, 오이카페에서 활동해 주시는 여러분 모두 응원해주셔서 감사합니다 🥒📲 🥰
iOS: https://apps.apple.com/us/app/oeee-cafe/id6754636117
Android: https://play.google.com/store/apps/details?id=cafe.oeee
🧡 Elk v0.17 is out!
#️⃣ Showing followed tag badge
🗳️ Poll UI improvements
🔊 New preference to unmute videos by default
🐞 Over 30 bug fixes & localization updates
We're already preparing the new quote support. Stay tuned!
Read the full changelog at
→ https://github.com/elk-zone/elk/releases/tag/v0.17.0
Jaeyeol Lee shared the below article:
洪 民憙 (Hong Minhee) @hongminhee@hackers.pub
Optique 0.7.0 introduces enhancements focused on improving the developer experience and expanding its ecosystem for type-safe CLI argument parsing in TypeScript. This release brings automatic "Did you mean?" suggestions to help users correct typos, along with seamless integrations for Zod and Valibot validation libraries, ensuring more robust and efficient CLI development. Duplicate option name detection is now included to catch configuration bugs early, and context-aware error messages provide users with precise feedback. The update also features customizable shell completion naming conventions and improved line break handling in error messages. With these new features, Optique aims to streamline CLI development in TypeScript, making it more intuitive and less error-prone. This release underscores Optique's commitment to providing developers with powerful tools for building high-quality CLI applications.
Read more →아… 젠부 귀찮다 그나저나 후쿠오카에서 Wagashi를 먹어보지 못한게 아쉽군… 다음엔 디저트 투어를 해보러 갈까 싶다. 카페에서 먹는 몽블랑도 좀 궁금하고. 프랑스 식 제과는 크게 궁금하지 않은데 일본식 프랑스 제과 뭐 이런건 궁금하다.
Jaeyeol Lee shared the below article:
자손킴 @jasonkim@hackers.pub
이 글은 네트워크 계층과 애플리케이션을 연결하는 L4 전송 계층의 핵심 개념을 소개합니다. 포트 번호를 통해 애플리케이션을 식별하고, UDP와 TCP 프로토콜의 특징과 패킷 형식을 설명합니다. UDP는 실시간성을, TCP는 신뢰성을 중시하며, TCP는 3-way handshake로 연결을 설정하고, 흐름 제어, 혼잡 제어, 재전송 제어를 통해 데이터 전송을 관리합니다. 특히 TCP 커넥션의 상태 전이 과정과 4-way handshake를 통한 연결 종료 과정을 상세히 다룹니다. 이 글을 통해 독자는 L4 전송 계층의 작동 방식과 TCP의 신뢰성 있는 데이터 전송 메커니즘에 대한 깊이 있는 이해를 얻을 수 있습니다.
Read more →I love the fact that you can version control configuration. I especially love how NixOS can allow you to configure your entire system declaratively (despite all Nix's other problems). The frustrating thing is that configuration must be edited via plaintext. I'd much rather click through a menu and have tick boxes and numerical inputs that constrain the config values by types and bounds. If I could version control everything that's configurable through a menu, I feel like life would be much better.
오랜만입니다! 기사를 읽다가 재밌어서 공유합니다.
Jaeyeol Lee shared the below article:
조내일 @tomorrowcho@hackers.pub
맥북에서 윈도우 키보드의 오른쪽 Alt 키를 한/영 전환 키로 사용하기 위한 설정 과정을 소개합니다. macOS 기본 설정으로는 왼쪽과 오른쪽 Option 키를 개별적으로 제어할 수 없어 Karabiner-Elements를 사용한 사용자 정의 키 매핑이 필요합니다. Karabiner 설치 후, Simple Modifications을 통해 right_option 키를 F18로 매핑하고, macOS 키보드 단축키 설정에서 '입력 소스 선택'을 F18로 지정해야 합니다. 만약 F18 키가 제대로 등록되지 않는다면, Karabiner의 드라이버 확장 프로그램 권한이 허용되었는지, 그리고 Devices 탭에서 외부 키보드의 'Ignore vendor events' 옵션이 활성화되었는지 확인해야 합니다. 이 설정을 통해 윈도우 환경에 익숙한 사용자도 맥에서 편리하게 키보드를 사용할 수 있습니다.
Read more →오늘
@fossforall 컨퍼런스 2025에서 發表한 〈야크 셰이빙: 새로운 오픈 소스의 原動力〉의 슬라이드를 共有합니다! 들어주신 분들 모두 感謝합니다!