개발
ps가 0.31GB라고 말하던 프로세스가 실은 20GB를 쓰고 있던 일, 성공 메시지를 내면서 자동화 전체를 꺼버린 배포 명령어, 분명히 지운 글이 계속 살아 있던 캐시 위치. 연구를 굴리는 시스템에서 실제로 밟은 함정이 여기 있습니다.
launchctl bootstrap으로 LaunchAgent 등록하기 — KeepAlive가 크래시를 75초 만에 살린 plist
launchctl load 대신 bootstrap으로 LaunchAgent를 등록하는 세 줄과, KeepAlive가 Metal 메모리 부족 크래시를 75초 만에 다시 띄운 실측. plist 안에서 ~는 안 풀리고, 파이썬 서버 로그는 stderr 쪽에 다 쌓입니다.
launchctl kickstart로 재시작했는데 설정이 그대로일 때 — plist는 bootout 뒤 bootstrap
launchctl kickstart -k로 재시작해도 고친 plist는 반영되지 않습니다. kickstart는 plist를 다시 읽지 않고, 반영하려면 bootout 뒤 bootstrap입니다. ps -o command=으로 인자를 확인하는 법과 32B 모델 재시작 한 번의 값 5분 57초.
맥미니 로컬 LLM 상시 운영 설정 — mlx_lm.server를 launchd로 149일 굴린 plist
mlx_lm.server를 launchd로 149일 돌리며 README에 없던 다섯 가지 — bootstrap과 KeepAlive, plist에는 셸 환경이 없다, kickstart는 plist를 다시 읽지 않는다, KV 캐시 상한 두 줄, 메모리는 ps가 아니라 footprint(23MB 대 23GB).
LLM 분류가 틀리면 모델 탓일까 — 범인은 LLM 후처리의 정규식 188건
실적이 43% 줄었다는 공시가 어닝 서프라이즈로 분류돼 있었습니다. LLM 출력을 먼저 의심했지만 태그도 방향도 정확했습니다. 범인은 그 뒤에서 도는 정규식 정규화 계층의 first-match-wins였고, 188건이 같은 경로로 뒤집혀 있었습니다.
wrangler Missing entry-point 오류 — Pages와 Workers 동명
GitHub PR의 Cloudflare Workers Builds 체크가 wrangler Missing entry-point로 실패하는데 Pages 배포는 성공한다면, 같은 레포에 Workers와 Pages가 같은 이름으로 둘 연결된 상태입니다. 원인, 해제 방법, 그리고 4일 동안 틀렸던 판단.
DART 공시 시간이 전부 09시일 때 — rcept_dt에는 시각이 없다
OpenDART의 rcept_dt에는 시각이 없습니다. 변환 단계가 09:00을 기본값으로 채워 공시 8만여 건의 발행 시각이 100% 오전 9시가 됐습니다. DART 공시 시간을 확인하는 법과, 이 '그럴듯한 값'이 결측보다 위험한 이유. 룩어헤드 편향은 발견되지 않습니다.
PostgreSQL array_length가 빈 배열에서 0이 아니라 NULL인 이유 — cardinality와의 차이
PostgreSQL에서 array_length(빈 배열)은 0이 아니라 NULL이라 WHERE 조건에서 조용히 빠집니다. 데이터만 백필하고 코드를 안 고쳐 나흘 만에 같은 상태로 돌아온 버그와, 석 달간 보이지 않았던 이유.
Cloudflare Tunnel과 Access Service Token으로 Vercel에 집 서버 붙이기
Vercel의 Next.js에서 Cloudflare Access 뒤 API를 fetch하면 401이 아니라 302와 로그인 HTML이 오고, 에러는 Unexpected token '<' JSON 파싱 실패로 위장합니다. Cloudflare Tunnel과 Access Service Token으로 포트 개방 없이 집 서버를 붙인 설정.
launchd cron이 command not found로 죽을 때 — PATH가 없다
launchd 잡이 exit 127, node: command not found로 8일간 실패했는데 로그 파일은 매번 생겼습니다. launchd는 ~/.zshrc를 읽지 않아 nvm의 PATH가 없습니다. plist에 PATH를 주는 법과, 버전 경로를 박는 해법이 다음 실패를 예약하는 이유.
Cloudflare Analytics CLS 점수가 나쁨일 때 — Cumulative Layout Shift 검산법
Cloudflare Web Analytics가 CLS를 '나쁨'으로 표시할 때 P50부터 P99까지 전부 1인 이유는 비콘이 Math.ceil로 값을 올림하기 때문이었습니다. 대시보드 CLS 점수를 믿기 전에 확인할 것과 실제 CLS를 재는 법.
Astro data-store.json — 삭제한 글이 빌드에 계속 남는 이유
Astro에서 삭제한 글이 빌드 결과와 sitemap에 계속 남는다면 원인은 node_modules/.astro/data-store.json입니다. .astro/와 dist/를 지워도 안 사라지는 이유를 재현하고, 해결책 세 가지와 내 프로젝트가 이 상태인지 확인하는 법을 정리했습니다.
블로그 원고의 숫자를 DB와 자동 대조하고 유출을 막는 CI
블로그 원고의 숫자를 PostgreSQL과 자동 대조하고, 맞아서 문제인 값(비공개 파라미터)은 정규식 스캐너로 막는 발행 파이프라인입니다. 검증→제거→스캔→쓰기 순서가 왜 그래야 하는지, 내가 만든 가드가 가드가 아니었던 이유.
LLM API 비용 줄이기 — 10만 건 분류에 $125 쓴 3단 라우팅
공시·뉴스 104,659건 분류에 API 비용 $125.66. 규칙→로컬 LLM(MLX)→상용 API 3단 라우팅으로, 전부 API였을 때의 $786 대비 약 16%입니다. LLM API 비용을 줄인 구조, 규칙이 59.3%를 거르는 이유, 넉 달 뒤 찾은 빠진 규칙 하나.
mlx_lm server 메모리가 계속 늘 때 — MLX KV cache 상한 설정
mlx_lm 서버가 64GB 맥미니 메모리를 고갈시켰는데 ps의 RSS는 0.31GB였습니다. 원인은 상한 없는 KV 캐시. RSS와 실제 점유가 다른 이유, mlx kv cache 제한 설정, 그리고 '느린 게 정상'이라는 결론까지.
n8n import workflow 뒤 실행이 안 될 때 — n8n CLI의 active 컬럼 하나
n8n import:workflow로 배포하면 워크플로가 비활성화됩니다. CLI에 활성화 옵션이 없고, active 컬럼 하나가 하루 409회 스케줄 전체를 끕니다. n8n import 뒤 실행이 안 될 때 확인할 스위치 두 개.