개발환경 · 워크플로 트러블슈팅
겪은 것만 씁니다.
검색해도 안 나오거나, 나와도 내 환경에선 안 통했던 문제들의 기록. 에러 메시지 원문과 재현 명령어, 그리고 실제로 통한 해결책까지 남깁니다.
카테고리
최근 글
원격 데몬에서 워크트리별 *.localhost dev 서버가 안 열리는 이유 — 릴레이는 HTTP 를 안 나른다
원격에 띄운 오케스트레이터 데몬이 워크트리마다 dev 서버를 올려도 클라이언트에서 그 화면이 안 열린다. 릴레이가 서비스 HTTP 를 터널링하지 않는 경계와, public 프록시로 여는 검증 방법.
Paseo 의 Project·Workspace·Tab 을 worktree 작업 단위로 끊는 법
같은 폴더에서 paseo run 을 반복하면 워크스페이스가 계속 새로 생긴다. 계층이 무엇에 대응하는지, 1 워크스페이스=1 워크트리=1 브랜치를 어떻게 지키고 CLI 로 검증하는지.
Paseo 페어링이 timeout 날 때, 릴레이 장애부터 의심하지 마라
셀프호스팅 Paseo 데몬에 폰을 페어링하는데 Connection timed out. 릴레이가 죽었나 싶지만 대개는 Server ID 불일치다. 릴레이를 의심하기 전에 봐야 할 진단 순서.
Paseo 에서 계정별 env 가 resume 뒤 사라진다 — provider override 로 고정하기
paseo run --env 로 넣은 CLAUDE_CONFIG_DIR 은 에이전트 생성 순간에만 산다. 재시작·resume 하면 조용히 데몬 기본 계정으로 돌아간다. 며칠 뒤 폰에서 이어 쓰면 엉뚱한 계정으로 청구되는 형태라 눈치채기 어렵다.
브라우저 없는 원격 서버에서 Codex CLI 로그인 — device authorization
ssh 전용 헤드리스 서버에서 codex login 은 로컬 콜백 서버를 띄우고 브라우저를 기다리다 끝나지 않는다. --device-auth 로 코드만 옮겨 로그인하고, auth.json 과 login status 로 실제 완료를 검증한다.
AI CLI 의 --resume 이 세션 ID 를 알아도 못 찾는 이유 — cwd 스코프
세션 ID 를 정확히 넘겼는데 No conversation found 가 뜬다. 세션이 작업 디렉터리 단위로 스코프되기 때문이다. 최소 재현과 실제 해결.
헤드리스 AI CLI 의 인증 실패가 "정상 빈 결과"로 위장한다 — subprocess returncode 함정
cron 에서 돌던 AI CLI 배치가 열흘간 조용히 전량 실패했다. 로그엔 에러가 없었다. OAuth 세션이 만료됐는데 호출부가 종료코드를 안 봐서, 401 로 죽은 빈 stdout 이 도메인상 정상 결과와 구분 불가능하게 섞인 게 원인이었다.
Claude Code 설정을 여러 기기로 옮기기 — 통째 복사 대신 4분류
AI CLI 하네스(룰·훅·플러그인·시크릿)를 여러 기기에서 재현할 때, config·인증·캐시·시크릿을 어떻게 분리해야 조용히 안 깨지는지.
systemd 로 띄운 AI CLI 의 bash 도구에서만 명령이 PATH 에서 사라진다
터미널에선 잡히는 명령이 에이전트 bash 도구에선 not found. 원인은 systemd → 데몬 → 셸 스냅샷으로 이어지는 4중 PATH 레이어였다.
기본 브랜치를 바꿨는데 새 worktree 가 옛 브랜치에서 분기될 때
로컬 clone 의 refs/remotes/origin/HEAD 는 clone 시점에 한 번만 설정되고 이후 자동 갱신되지 않는다. origin/HEAD 를 보고 분기하는 도구는 전부 옛 기본 브랜치를 쓴다.
한글 디스플레이 폰트 + -webkit-text-stroke 에서 없던 점이 생긴다
Google Fonts Gugi 로 바꾸자 글자 사이에 작은 점이 렌더링됐다. stroke 를 빼면 사라진다. 글리프의 분리된 서브패스에 외곽선이 독립적으로 그려지기 때문으로 보인다.
PR 에 Closes #N 을 써도 이슈가 안 닫힌다면 — base 가 기본 브랜치인지 본다
GitHub 의 closing keyword 는 PR 의 base 가 저장소 기본 브랜치일 때만 동작한다. develop 기반 워크플로로 옮기는 중이면 조용히 무시된다.
Playwright 로 크로스탭 동기화를 테스트할 수 없었던 이유 — browser.newPage()
browser.newPage() 는 호출할 때마다 새 BrowserContext 를 만든다. 같은 오리진이어도 localStorage 와 BroadcastChannel 이 전혀 공유되지 않는다.
유실된 줄 알았던 리서치는 세션 트랜스크립트에 남아 있다
서브에이전트에게 시킨 대량 조사에서 최종 요약만 기록하고 원본은 버린 줄 알았는데, 세션 JSONL 은 삭제되지 않고 영구 보존된다. 위치와 파싱 방법.
transform: scale 로 줄인 iframe 이 flex 부모를 밀어낼 때 — min-width: auto
transform 은 레이아웃에 영향을 주지 않는다. 고정폭 iframe 의 원래 폭이 flex item 의 min-content 크기로 잡혀 flex-basis 를 무시하고 컬럼을 아래로 떨어뜨린다.
z-index: -1 배경 레이어가 안 보인다 — body 배경색이 캔버스로 승격될 때
position:fixed + z-index:-1 로 깐 배경이 Chromium 에서 body 배경색에 덮인다. getComputedStyle 로는 전부 정상으로 보여서 스크린샷 없이는 못 잡는다.
pptx 화면설계서 버전 diff — 텍스트 추출과 도형 좌표로 실제 변경점 찾기
슬라이드 100장짜리 설계서가 v5.2 에서 v5.3 으로 올라왔다. 눈으로 대조하는 대신 슬라이드 단위로 텍스트를 뽑아 diff 를 뜨고, 도형 좌표로 항목 순서를 판정한다.
Claude Code auto mode 가 셀프 머지를 막는다 — 우회하지 말 것
에이전트가 자기가 만든 PR 을 병합하려 하면 차단된다. 차단을 풀려고 설정을 고치는 것도, 그 설정 파일을 읽는 것도 막힌다. 버그가 아니라 의도된 경계다.
dev 서버가 돌고 있는데 worktree 디렉터리가 통째로 사라졌다
실행 중이던 vite 프로세스의 cwd 가 (deleted) 로 바뀌고 worktree list 에서 항목 자체가 소멸했다. 원인은 특정하지 못했고, 복구도 불가능했다. 운용 원칙으로 대응한다.
Vite ssrLoadModule 로 브라우저 없이 앱 모듈 통합테스트 — 그리고 잡아낸 tombstone 버그
import.meta.env 를 참조하는 앱 소스는 plain node 로 import 하면 터진다. Vite dev 서버의 ssrLoadModule 로 실제 소스를 Node 에서 그대로 실행하면 브라우저 없이 함수 단위 검증이 된다.
30MB JSON 을 import 했더니 tsc 가 heap OOM — ?url + fetch 로 우회
resolveJsonModule 은 JSON 값에서 리터럴 타입을 통째로 추론한다. 런타임과 번들링은 멀쩡한데 타입체크만 죽고, 힙을 키워도 해결되지 않는다.
next/og 아이콘을 폰트 주입 없이 굽기 — SVG data-URI 트릭
Satori 는 시스템 폰트 폴백이 없어 넘긴 fonts 에 없는 글자를 두부로 그린다. 아이콘이 글자가 아니라 도형이면 폰트가 아예 필요 없다.
orval 의 fetch 클라이언트는 500 을 성공으로 넘긴다 — mutator 로 throw 주입
httpClient fetch 로 생성한 코드는 res.ok 를 확인하지 않고 status 무관하게 resolve 한다. react-query 는 throw 해야 에러로 인식하므로 4xx/5xx 가 전부 isSuccess 가 된다.
claude mcp add 로 등록했는데 목록에 안 보인다 — 기본 scope 는 local
MCP 서버를 등록해도 다른 디렉터리에서 연 세션에서는 안 보인다. 기본 scope 가 local 이라 실행한 cwd 아래에만 저장되기 때문이다.
worktree 마다 .env 를 다시 만들지 않기 — post-checkout 훅으로 심볼릭 링크
git worktree add 는 tracked 파일만 체크아웃하므로 gitignore 된 .env 는 절대 따라오지 않는다. 래퍼 함수 대신 post-checkout 훅을 쓰면 어떤 방식으로 만들든 자동으로 연결된다.
라이브러리 없이 칼럼 드래그 정렬 만들기 — 행 기반 그리드에서
dnd-kit 없이 Pointer Events 만으로 칼럼 순서 변경을 구현했다. 칼럼이 단일 DOM 이 아닌 row-major 그리드에서, 리렌더 없이 60fps 를 유지하는 방법.
multiple candidate TSConfigRootDirs — 저장소 안에 worktree 를 만들면 ESLint 가 전부 터진다
repo 내부에 git worktree 를 두면 tsconfig.json 이 두 벌 존재해 typescript-eslint 의 tsconfigRootDir 자동탐지가 후보를 결정하지 못한다. 208개 파일이 전부 파싱에러.
worktree 여러 개를 동시에 굴리는 dev 서버 전략 — pm2 를 버린 이유
pm2 는 프로세스 이름이 전역이라 같은 프로젝트의 worktree 를 동시에 띄울 수 없고, 시작 시점 PATH 를 박아둬서 fnm 과 충돌한다. 경로 해시 기반 결정적 포트 스크립트로 교체했다.
WSL cron 에서 git push 가 멈출 때 — GCM 은 GUI 인증기다
터미널에서는 되는 git push 가 cron 에서만 실패한다. 전역 credential helper 가 Windows GCM 이라 인증창을 띄울 수 없어서다. repo 단위로 gh 토큰 helper 를 붙여 해결한다.
TrafficMonitor AI Usage 플러그인이 WSL2 에서 멈추는 두 가지 이유
Codex 값은 세션 jsonl 경로가 WSL 안에만 있어서, Claude 값은 헬퍼가 PowerShell 5.1 에서 DPAPI 복호화에 실패해서 멈춘다. 원인이 완전히 독립적이라 따로 고쳐야 한다.
LLM 이 만든 UI 가 "AI스러워" 보이는 이유와, 단계를 쪼개는 전략
카드 남발·과한 라운드·보라 그라데이션은 우연이 아니다. 원인 패턴을 정리하고 ground-up 과 redesign 두 상황에서 각각 다른 워크플로를 쓴 기록.
CapsLock + 알파벳으로 앱 전환하기 — Kanata 와 AHK 를 나눠 쓴 이유
AutoHotkey 단독으로 CapsLock 을 modifier 로 쓰면 토글이 새거나 오발이 난다. Kanata 로 드라이버 수준에서 조합키로 바꾸고 AHK 는 앱 전환만 맡기면 안정된다.
WSL2 에서 Windows 토스트 알림 띄우기 — 외부 모듈 없이
BurntToast 나 wsl-notify-send 설치 없이 Windows 내장 WinRT API 를 powershell.exe 로 직접 호출해 데스크톱 알림을 띄운다. 한글 깨짐은 UTF-16LE base64 인코딩으로 잡는다.
tmux 안에서만 OSC 52 복사가 안 될 때 — set-clipboard external
tmux 밖에서는 되는 OSC 52 클립보드 복사가 tmux 안에서만 무음 실패한다. tmux 3.4+ 기본값 set-clipboard external 이 안쪽 앱의 OSC 52 를 호스트 터미널로 전파하지 않기 때문이다.
WSL2 에 Obsidian Linux 빌드 올리기 — FUSE·AppRun·한글 폰트 세 함정
Windows Obsidian 으로 WSL 폴더를 열면 9P 오버헤드와 watcher 불안정을 겪는다. WSLg 로 Linux 빌드를 띄우면 해결되지만 AppImage 추출·바이너리 직접 실행·CJK 폰트 링크가 필요하다.
WSL2 → Windows 클립보드 한글 깨짐 — clip.exe 는 CP949 로 읽는다
WSL2 에서 clip.exe 로 한글을 복사하면 메모장에서 깨진다. 원인은 시스템 ANSI 코드페이지 해석. PowerShell Set-Clipboard 또는 UTF-16LE BOM 으로 해결한다.