← 작업일지

2026-07-04 · v1.0.0

2026.07.04 작업일지 v1.0.0

02:06, 자기 전에 앱 심사만 걸어두려다 스크린샷 한 장에서 옛날 화면을 발견했다. 07:29, 제출을 막던 진짜 범인은 스샷도 구독도 아닌, 애플이 갱신한 계약서 재동의였다. 그 사이 낮에는, 다른 컴퓨터들이 올린 코드 열 몇 개를 하나씩 내 손으로 다시 돌려보며 병합했다.

7월 4일은 두 개의 이야기가 겹친 날이었다. 하나는 새벽 내내 이어진 한줄일기 (App Store) iOS 심사 대장정, 다른 하나는 낮부터 저녁까지 이어진 자동화 인프라 버그 사냥과 병합(merge) 게이트 마라톤. 하루 동안 여러 저장소에 62개의 커밋이 쌓였고, 그 대부분은 작업 노드들이 올린 제출서(PR)를 검증하고 합치는 과정에서 나왔다. 결론부터 말하면 — 앱은 계약서 서명 하나로 벽을 뚫었고, 인프라는 “초록불이라는 워커의 말”을 곧이곧대로 믿지 않은 덕에 여러 함정을 피했다.

1. 자정의 스위치 — “합쳤어”를 자동으로

하루의 시작은 자정 직후였다. 작업 노드 한 대가 올린 기능(PR#384) — 내가 무언가를 병합할 때마다 손으로 “합쳤다”고 알려주던 걸, 자동으로 폰과 봇 채팅방에 쏴주는 알림으로 만드는 것이었다. 인프라 성격이라 서브에이전트에게 정밀 리뷰를 맡겼고 8개 항목 전부 통과(직접 API 호출 0, 플래그 없으면 발신 0인 이중 게이트, 실패 시 loud 전파 등)를 확인한 뒤 6b75e80으로 병합했다.

스위치를 켜자마자 스모크 발신을 한 방 쐈고, 폰에 실제로 알림 두 건이 도착한 스크린샷으로 실도착까지 확인했다. 코드 검사가 통과해도, 실전에서 엉뚱한 방으로 가지 않는지는 결국 한 번 켜서 눈으로 봐야 한다.

2. 새벽 두 시, 스크린샷이 멈춘 심사

“심사는 며칠 걸리니 자기 전에 걸어두자”는 한마디로 한줄일기 iOS 제출이 시작됐다. 준비 상태는 100%로 보고됐고(빌드 valid, 구독 2종 완비), 마지막 제출 버튼만 남은 상황. 그런데 제출 직전 화면 스크린샷에서 상품 페이지 하단에 v1.1.0 — 며칠 전 지적됐던 옛날 버전 스샷이 iOS에는 아직 남아 있었다.

이미 한 번 제출이 나간 뒤였다. 옛 스샷 + 첫 구독앱의 “구독 동반 제출 누락” 가능성이 겹치면 애플 가이드라인 2.1(b)로 반려될 위험이 있어, 나간 제출을 취소하고 재정비에 들어갔다. 6.5인치 스샷을 현행으로 교체하고 URL 세 종류를 옛 도메인에서 현재 주소로 갈았다. 그런데 구독은 계속 MISSING_METADATA. 이름·가격·설명·심사용 자료를 전부 채웠는데도 풀리지 않았다.

3. 진짜 벽은 계약서였다

처음 세운 가설은 “유료 앱(Paid Apps) 계약 미체결”이었다. 첫 유료 상품의 전형적 패턴이라 유력해 보였다. 하지만 아침에 App Store Connect 웹을 직접 진단해 보니 — 유료 앱 계약은 오히려 정상·활성이었고, 진짜 범인은 따로 있었다. **개발자 프로그램 사용권 계약(Apple Developer Program License Agreement)**을 애플이 갱신했고, 계정 소유자의 재동의가 필요한 상태. 이 하나가 구독 활성화와 제출을 동시에 막고 있었다.

계정 화면의 배너에서 약관에 서명하자 애플이 곧바로 “Agreement signed: Apple Developer Program License Agreement” 확인 메일을 보냈다(08:10). 그 순간 벽이 뚫렸다. 흥미로운 대조 — 안드로이드는 이미 앞서 있었다. 같은 앱의 구독 상품 월 ₩3,300·연 ₩29,900이 스토어에 이미 판매 활성 상태였고, 빌드도 준비 완료였다.

배운 것 — 막힌 화면이 가리키는 곳과 진짜 원인이 같은 경우는 드물다. 스샷을 의심하고 구독을 의심했지만, 정작 범인은 눈에 안 띄는 계약서 재동의였다. 유력한 첫 가설(유료 앱 계약)일수록 실측으로 반증될 준비를 해둬야 한다.

4. “안전한 건 묻지 마” — 그리고 실측이 워커를 이긴 순간들

낮에는 흐름이 완전히 바뀌었다. 세션을 비우고 다시 켜면 밀린 작업을 스스로 이어받는 구조 덕에, 대기 중이던 30여 개 할 일을 훑고 메모요 (App Store) 월 구독(프리미엄) 구현 계획을 10개의 테스트 우선(TDD) 태스크로 문서화했다. 그다음부터는 작업 노드들이 올린 제출서가 줄줄이 도착했다.

훅(자동 장치) 버그를 고친 PR이 연달아 왔다 — device-id 훅이 자기 자신을 ”🤖 unknown”으로 인식하던 버그(PR#386), 형제 훅 동종 수리 + 이 버그 계열을 아예 봉인하는 자동 검사기(PR#387), 공유 헬퍼로 리팩터링(PR#388). 훅 수정은 폰으로 알림이 나가는 R3라 원래 매번 물어봐야 하지만, “안전한 건 묻지 마”라는 한마디로 새 상시 규칙이 섰다: 풀그린 + 가역 + enforcement 무변경 + 소범위, 이 네 조건을 다 만족하는 훅 픽스는 앞으로 내가 직접 검증하고 자동 병합한다.

핵심은 매번 워커의 “초록불”을 그대로 믿지 않은 것이다. 한 PR은 워커 쪽에서 Ran 299 OK였지만 내 기계에선 처음에 에러가 떴다 — 알고 보니 pytest 미설치로 인한 모듈 로드 실패, PR과 무관한 환경 차이였다. 또 다른 병합 뒤에는 중첩된 유령 워크트리가 정책 파일(POLICY.md)을 7개에서 15개로 오염시킨 걸 발견했다. 저장소 안에 오래된 전체 복사본이 하나 살아 있었고, 정책 생성기가 그걸 긁어 들이고 있었다. 생성기를 “git이 추적하는 파일만 스캔”하도록 고쳐(PR#392, f5b87a5) main을 깨끗한 7개로 복구했다.

배운 것 — 워커의 ‘초록불’은 그 기계에서의 초록불이다. 게이트는 같은 코드를 내 손으로 다시 돌려볼 때만 게이트다. RED→GREEN을 직접 재현하지 않은 통과는 통과가 아니다.

가장 섬뜩했던 건 테스트가 진짜로 새어나가던 문제였다. 반복해서 뜨던 정체불명의 “[Codex result return]” 메시지의 근원을 추적하니, 테스트 픽스처가 격리 없이 라이브 세션에 실제로 주입되고 있었다. 한 테스트의 두 번째 호출이 로컬 노드 환경변수를 빠뜨려, 메인 노드에서 돌면 진짜 tmux 붙여넣기를 실행하고 있었던 것. stop-the-bleed로 격리를 채워 막았다(PR#398). 동시성 잠금 PR(#400)에서는 워커의 리눅스에선 안 보였던 SIGPIPE 플래키(간헐 실패) 테스트를 잡았다 — cat ... | grep -qpipefail이 걸려 8번 중 5번 죽던 것을, 파이프를 없애 25/25 안정으로 만들었다. 잠금 자체는 매번 승자 1명 불변식을 지키고 있었으니, 고장 난 건 테스트였다.

5. supply 라는 단어를 막아버린 문지기

저녁의 하이라이트는 빌드 잠금 문지기(build-lease guard) PR(#401)이었다. 스토어 빌드 명령을 가로채는 항상-켜진(PreToolUse) 방어 훅인데, 리뷰 중 과잉 차단 버그를 잡았다. 패턴 \bsupply\b가 명령어 어디에 있든 “supply”라는 단어만 있으면 막아버려서 — git commit -m "fix supply-chain-logic", echo supply and demand, cat docs/bundletool-notes.md 같은 멀쩡한 명령까지 전부 DENY됐다. 실행 맥락으로 좁혀(\bsupply\b 제거, bundletool은 명령 위치/.jar 형태로 앵커) 13개 케이스가 전부 올바르게 통과/차단되도록 고쳤다.

그런데 이 PR에는 “머지 금지, 수동 게이트 유지” 지시가 걸려 있어서, 검증은 끝냈지만 **병합은 보류(HELD)**했다. 이 과정에서 하나 더 배운 게 있다 — ~/.claude/hooks가 저장소 훅으로 심볼릭 링크돼 있어, 브랜치를 체크아웃하는 것만으로 그 문지기가 내 세션에 즉시 살아나 나를 막았다. 배포 전 반드시 알아둬야 할 라이브-온-체크아웃 특성이었다.

배운 것 — 안전장치가 과하게 막으면 그건 안전이 아니라 고장이다. 단어 하나(supply)가 멀쩡한 커밋을 막았다. 방어 훅은 “무엇을 막느냐”만큼 “무엇을 막지 않느냐”를 테스트로 못 박아야 한다.

6. 그래서 뭐가 남았나

숫자로 보면 하루에 여러 저장소 62개 커밋, 병합·검증한 제출서 열 몇 개. 하지만 남은 건 숫자가 아니라 몇 가지 구조적 개선이다.

갈래결과
한줄일기 iOS계약(PLA) 재동의로 구독 벽 해제 → 재정비 후 제출 준비 완료(스샷·URL·구독 필드), 마지막 제출 버튼만 남음
한줄일기 Android구독 월 ₩3,300·연 ₩29,900 이미 활성, 빌드 준비 완료
자동화 인프라훅 버그 계열 봉인, 정책 파일 오염 복구, 테스트 유출 격리, 플래키 테스트 안정화, 봇 토큰 중앙화, 병합 4단계 래퍼(자기 자신으로 검증)
정책”안전한 훅 픽스 자동 병합” 규칙 확립, CPU 무거운 작업은 특정 노드 우선 배정 규칙 등재

일과 후에는 홈페이지 인사이트에 글 하나를 더 얹었다 — “Block은 3,500명을 안 가르쳤다: 50명으로 조직 전체를 자율화한 AI 챔피언 전략.”

다음 세션으로 넘긴 것들: 메모요 월 구독 구현(계획은 완성), 못자요 앱의 월/연 구독 분리(코드 완료, 병합 보류), 텔레그램 브릿지 종합 정비(리뷰에서 버그 20개·근본원인 2개 발굴), 그리고 한 번 롤백했던 코덱스 재절체 카나리의 재도전.


다음 이야기는 메모요의 첫 유료 구독과, 브릿지 대정비 이야기.

— 강대종 / @ssamssae