Joyuno's Dev Blog

2026-08-05 (수) 개발일지

dev-blog

오늘은 옵시디언 정리 프로젝트와 lepons 에이전트 플랫폼을 함께 손봤다. 전자는 미분류 논문 자동 분류 파이프라인을 완성하고 재발행 워크플로를 검증하는 일이었고, 후자는 TUI 풀스크린 모드가 마우스 입력을 먹는 문제를 인라인 모드로 전환해 고치는 작업이었다. 두 프로젝트 모두 초기 가설이 틀렸고, 문제의 실제 원인을 찾으려면 한 단계 더 깊이 파봐야 했다.

옵시디언 논문 큐레이션 — 모지바케 출처 표기와 미분류 자동 분류 완성

GeekNews 주간 논문 요약을 메인 블로그에 재발행할 때 한글 출처 표기가 깨져 보였다. 처음엔 단순 인코딩 문제라고 생각했는데, 파보니 주간 발행 워크플로 자체가 원문을 제대로 읽지 못하고 있었다.

main_weekly.py 의 발행 로직을 다시 따라가보니, geeknews_papers_collector.py 에서 수집한 출처 메타데이터가 JSON 으로 제대로 저장되고 있었다. 그런데 발행 단계에서 그 필드를 참조하지 않고 있었다. 결국 큐레이션 출처를 명시적으로 읽는 라인을 발행 템플릿에 추가해야 했다. 수정 후 재발행을 돌렸고, 이번엔 인용줄이 정상 한글로 떴다:

큐레이션 출처: GeekNews · ninebow — [2026/06/08 ~ 14] 이번 주에 살펴볼 만한 AI/ML 논문 모음

더 큰 작업은 미분류 논문 39개를 자동으로 도메인 그룹에 할당하는 것이었다. 처음엔 모든 과거 글의 OG 이미지를 backfill 하는 방식으로 접근했다. 94개 글 중 80개는 잘 받았지만, 14개가 여전히 실패했고 — 무엇보다 저장소를 OG 이미지 파일로 채우고 싶지 않다는 제약이 있었다. 방향을 바꿨다.

진짜 문제는 외부 호스트(opengraph.githubassets.com)의 가용성이 단일 장애점이라는 걸 깨달았다. 그걸 완전히 피할 순 없지만, 빌드 시점에 죽어있는 경우만 골라서 self-host 하는 게 맞는 전략이었다. gen_graph.py 의 _self_host_image() 함수에 HTTP HEAD 검증을 넣었다:

  • status 200 + content-type image/* + content-length ≥ 5KB → 외부 URL 그대로 사용
  • 조건 불만족 → assets/og/ 로 다운로드 후 GitHub Contents API 로 commit

SVG 와 뱃지 이미지(skills.sh, forthebadge.com)는 별도로 차단해 의미 없는 다운로드를 방지했다.

검증 결과: | 항목 | 변화 | |——|——| | 평소 빌드 시 저장소 수정 | 없음 (clean) | | 외부 호스트 죽은 글만 | self-host 처리 (1건) | | 나머지 글 | 외부 URL 유지 |

오늘 빌드에서 깨진 글이 정확히 1건만 fallback 으로 들어갔고(assets/og/2026-05-17-...png), 나머지는 그대로 살아남는 걸 확인했다. 14개 OG 가 여전히 429 로 막혀있긴 한데, 며칠 뒤 재시도 cron 으로 돌릴지는 검토 중이다.

lepons cockpit — 풀스크린 TUI 마우스 입력 문제를 인라인 모드로 해결

cockpit 앱의 TUI 에서 마우스 드래그가 먹지 않는 문제가 있었다. 처음엔 이벤트 리스너가 제대로 등록되지 않았거나 터미널 설정이 꼬였다고 생각했다. 하지만 문제는 "tui": "fullscreen" 설정 자체였다.

풀스크린 TUI 는 터미널의 대체 화면 버퍼(alternate screen buffer)를 쓰고 마우스 트래킹을 켠다. 그 결과 마우스 드래그가 터미널의 텍스트 선택이 아니라 Claude Code 의 입력으로 먹히고, 스크롤백 출력도 남지 않는다. 근본 원인은 풀스크린 모드의 터미널 격리 메커니즘 자체였다.

풀스크린 모드를 쓰는 한 마우스 입력은 앱이 독점하고, 터미널 기본 동작(텍스트 선택, 스크롤백)은 불가능하다.

세 가지 해결 옵션이 있었다. Shift+드래그 로 기존 동작을 우회할 수도, 터미널 설정을 바꿀 수도 있었지만, 가장 근본적인 해법은 풀스크린을 끄고 인라인 모드(tui: inline)로 전환하는 것이었다. 인라인 모드는 터미널 기본 버퍼를 그대로 쓰므로 마우스 입력이 터미널에 남고, 스크롤백도 정상 작동한다.

src/renderer/main.ts 에서 두 가지를 수정했다:

scrollback: 20000,   // 인라인 에이전트 출력이 기본 1000줄을 금방 넘긴다

에이전트 출력이 자주 1000줄을 초과하므로 스크롤백을 20000줄로 늘렸다.

if (pressed === "v") {
  event.preventDefault();          // ← 네이티브 붙여넣기 경로를 막는다
  term.paste(cockpit.readClipboard());
  return false;
}

네이티브 붙여넣기가 두 번 작동하는 문제(한 번은 터미널, 한 번은 앱)를 막기 위해 기본 경로를 차단했다.

검증 단계에서 실수가 있었다. 빌드는 이미 끝났는데(npm run build 완료) 앱은 여전히 이전 번들을 메모리에 들고 있었다:

상태 시간
dist/renderer.js 빌드 완료 17:27:12
electron.exe 실행 중 이전 번들 메모리

앱을 껐다 켜야 새 번들이 로드되는데, npm start 를 다시 돌려 재빌드와 함께 재시작했다. 그제서야 붙여넣기 중복이 사라지고 스크롤백이 정상 작동하는 걸 확인했다.

남은 과제는 두 가지다. 10번 서버(terasys)는 호스트는 살아있는데(known_hosts 에 있음) publickey 인증이 모두 거부되고 있다. 계정명 확인과 키 등록이 필요하다. 맥북은 주소를 모른다. lepons 전체 코드베이스를 훑어봐도 주소를 저장한 마커가 없다 — 아직 이 규약으로 기록한 적이 없거나 유예해둔 지름길이 정리되지 않은 상태다. 두 호스트 현황을 먼저 확인하고 번들을 새 설정으로 다시 만들 계획이다.

오늘의 한줄

처음 보인 증상(깨진 출처 표기, 먹지 않는 마우스)은 겉모습일 뿐 — 문제의 진짜 원인은 한 단계 더 안쪽(발행 파이프라인 누락, 터미널 버퍼 격리)에 있었고, 그걸 찾으려면 가설을 버리고 다시 파야 했다.