KO
블로그
JustSend를 만들면서 생긴 기록입니다.
iOS 앱 품질거래가 없는 알림이 URL 등록을 실패로 만든다서버 알림 웹훅은 이미 완비돼 있었다. 그런데 테스트 알림 하나가 400을 받아 등록이 실패로 표시될 상태였다. 두 구멍을 닫고 왕복으로 증명한 기록이다.
그 밖의 기록because 안의 cause: 부분 일치가 통과시킨 목록열 개 이름을 요구하는 검사가 이름별 부분 문자열 대조였다. 여덟 개만 열거한 문서도 10/10으로 통과했고, 경계를 붙인 한 문장이 그것을 막는다.
백엔드와 운영두 번 호출하면 증명이 바뀌었다: 실패 우선을 상태로 만든 기록GREEN이 RED 파일의 존재만 확인하던 동안 이미 끝난 증명이 아무 파일로 교체됐다. 전이를 상태로 바꾸고 뒷정리 영수증을 완료 조건에 넣은 과정이다.
iOS 앱 품질렉을 세 갈래로 갈랐다: hitch, hang, 그리고 없는 프레임사람이 실기를 만지는 동안만 관측해 구간별 커밋 단계 p95를 냈다. 프레임 예산 안에 있는 구간은 하나였고, '스크롤은 괜찮다'는 앞선 결론은 정정됐다.
iOS 앱 품질표를 그리는 코드는 다 있었다: 열한 번째 도구가 없는 기능이 되는 이유표 조판 계약 스물한 건이 초록인 동안 사용자는 표를 넣지 못했다. 보이는 칸 여덟 개가 기능의 존재를 판정한 자리를 코드와 실패한 테스트로 짚는다.
iOS 앱 품질380이냐 390이냐: 축을 잘못 잡으면 멀쩡한 것도 고치게 된다표가 지면을 채우지 않는다는 판단은 줄 상자 폭에서 나왔다. 그려진 픽셀로 다시 재니 이미 채우고 있었다. 같은 종류의 축 오류가 세 자리에서 났다.
데이터와 동기화표만 남은 기기: 알 수 없는 것을 아는 것처럼 말한 화면복구 코드가 어디에 있는지 화면이 단정하던 문구를 판정 가능한 것만 말하도록 고쳤다. 같은 실수를 세 번 저지른 자리와 그것을 막는 단언을 적는다.
그 밖의 기록default-src 'none'이 삼킨 미리보기 두 개공유 지면에서 PDF와 오디오 미리보기가 죽어 있었다. 원인은 복호나 저장이 아니라 정책 한 줄에 지시자 두 개가 없던 것이다.
iOS 앱 품질상류를 고칠 자리인가, 이 기기에만 있는 축인가Mac 앱은 iOS 정본을 고정 커밋에서 재생성한다. 핀을 올리자 유료 등급이 끊겼고, 고칠 자리를 한 번 잘못 지목했다. 그리고 그 수정이 새 결함을 만들었다.
백엔드와 운영image_path 한 줄이 목록과 상세 이미지가 되기까지caller의 이미지가 staging, attachment store, 목록과 상세 surface로 이동하는 데이터 흐름을 설명합니다.
백엔드와 운영미사용 API를 삭제하지 않고 유지·보류·재확인으로 분류한 이유등록 route를 consumer·traffic·기존 처분 근거로 판정해 세 outcome으로 나눈 API 감사 flowchart를 설명합니다.
iOS 앱 품질권한 심사 대응을 상태 머신으로 다시 읽기: 설명·동의·복구의 소유권첫 실행 pre-prompt 반려를 문구 문제가 아니라 notDetermined·granted·blocked 상태와 요청 소유권의 문제로 다시 분석합니다.
그 밖의 기록App Store 재제출을 한 번의 프로세스로 만드는 정합성 감사build, metadata, App Privacy, 구독과 공개 공유를 순차적인 재제출 gate로 묶어 검수한 과정을 정리합니다.
백엔드와 운영공개 DMG와 MCP를 안전하게 잇는 두 신뢰 경계의도 큐 인가와 콘텐츠 URL host policy를 하나의 보안 architecture에서 연결해 설명합니다.
그 밖의 기록JustSend seed를 Research Pack과 Evidence로 바꾸는 글쓰기 데이터 흐름작업 기록을 바로 글로 요약하지 않고 source expansion·claim mapping·Fidelity gate를 거치는 이유를 설명합니다.
그 밖의 기록Developer ID 직접 배포의 실제 위치: App DMG와 helper ZIP을 분리한 구조App Store 자격 충돌 뒤 App과 MCP helper를 서로 다른 공증 산출물과 runtime으로 배포한 구조를 설명합니다.
데이터와 동기화비동기 MCP 기록에서 노트를 잃지 않는 provisional·live·dead 상태 계약work_start 직후 note가 사라진 실패를 identifier 수명과 anchor 상태 전이로 다시 설계한 과정을 설명합니다.
시작과 구조 결정1인 개발 기술 스택: iOS 앱과 Go 백엔드, 쿠버네티스를 혼자 운영하는 경계1인 개발자가 iOS 앱과 Go 백엔드, 셀프호스팅 쿠버네티스를 함께 운영하며 정한 기술 스택 경계입니다. 메모의 세 글자를 두 코드 경로가 다르게 읽은 버그에서 시작합니다.
시작과 구조 결정Swift Package 모듈 분리: 코어를 분리했는데 앱이 로컬 경로를 읽던 문제Swift Package로 AI 코어를 앱에서 분리하면서 정한 경계와, 선언과 실제 빌드가 갈린 문제를 적었습니다. 함수 네 개와 실패 아홉 가지를 경계로 삼았습니다.
- 온디바이스 AIApple Foundation Models 시작하기: @Generable 구조화 출력 실측Apple Foundation Models로 온디바이스 LLM을 처음 붙이면서 availability 확인부터 @Generable 구조화 출력까지 실제 호출 모양을 좁혔습니다. 평문과 구조화 출력을 각각 다섯 번 재 비교합니다.
온디바이스 AI온디바이스 LLM 긴 문서 요약: 청킹과 map/reduce로 컨텍스트 한계 넘기컨텍스트가 좁은 온디바이스 LLM으로 긴 문서를 요약하려고 만든 청킹과 map/reduce 파이프라인입니다. 상한을 4,096으로 알고 조각 크기를 정했다가 다시 재게 된 경위를 적었습니다.
- 온디바이스 AIFoundation Models 에러 처리: guardrailViolation 폴백 설계Apple Foundation Models가 던지는 오류를 종류별로 나누고 폴백 경로를 만든 기록입니다. 평범한 한국어 문장이 guardrailViolation으로 거부된 일에서 시작합니다.
온디바이스 AI온디바이스 추론 발열 관리: thermalState 게이팅과 직렬 큐온디바이스 LLM 추론에서 기기가 뜨거워지는 문제를 admission queue 직렬화와 thermalState 게이팅으로 다뤘습니다. 한 번에 하나만 돌렸는데도 열이 오른 이유를 찾습니다.
- 온디바이스 AILLM 요약 품질 검증: 원문 복사율과 반복 판정을 코드로 재기LLM 요약이 쓸 만한지 사람 눈이 아니라 코드로 판정하는 기준을 세웠습니다. 원문을 거의 그대로 돌려준 요약이 품질 검사를 통과한 일에서 시작합니다.
- iOS 앱 품질iOS 온디바이스 OCR: Vision과 PDFKit으로 서버 OCR 제거하기iOS 온디바이스 OCR을 서버로 옮겼다가 다시 Vision과 PDFKit으로 되돌린 왕복 기록입니다. 개인정보처리방침과 코드가 어긋난 것을 확인한 시점에서 시작합니다.
- iOS 앱 품질iOS 앱 콜드 스타트 최적화: 873ms에서 482ms로 줄인 측정과 수정SwiftUI 앱 콜드 스타트를 873ms에서 482ms로 줄인 과정입니다. Instruments 대신 단계별 로그를 남기는 도구를 먼저 만들었고, 로그인 상태의 680ms까지 측정했습니다.
- iOS 앱 품질SwiftUI 메모리 추적: jetsam 로그로 725MB 원인 좁히기앱이 죽지 않은 채 화면 전환만 멈추는 현상을 jetsam 로그로 추적했습니다. 크래시 리포트가 없는 상황에서 생애 최대 725MB까지 좁힌 과정입니다.
- 데이터와 동기화SQLite FTS5 한국어 전문검색: trigram 토크나이저와 2글자 LIKE 폴백GRDB와 SQLite FTS5로 한국어 부분 일치 검색을 만들면서 unicode61에서 trigram으로 옮긴 기록입니다. 세 글자 미만 질의가 인덱스에 없어 남는 LIKE 경로까지 다룹니다.
iOS 앱 품질SwiftUI 디자인 토큰 런타임 교체: 호출부 367곳을 건드리지 않는 파사드static let 색 토큰 44개가 91개 파일에서 367번 불리는 상태에서 런타임 테마 교체를 만들었습니다. 호출부를 그대로 두고 값만 갈아타는 파사드 구조를 씁니다.
- iOS 앱 품질앱 테마 팔레트 감사: 웹 팔레트에서 가져온 20벌을 12벌로 줄인 기준웹 랜딩 팔레트에서 기계적으로 뽑은 유료 테마 20벌의 색이 앱에서 어긋난 이유를 찾아 12벌로 줄였습니다. 카드가 배경에서 사라진 8벌부터 확인합니다.
- 백엔드와 운영Go 웹 본문 추출: density 파서로 article 태그를 믿지 않기Go로 링크에서 본문만 뽑는 추출기를 만들면서 시맨틱 태그를 믿지 않게 된 경위입니다. article 태그 23개 안에 345자 위젯만 있고 본문 9,400자가 밖에 있었습니다.
- iOS 앱 품질StoreKit 구독 상태 동기화: 서버와 앱 판정이 갈리던 6경로콘솔은 pro인데 앱은 free로 보이던 증상 하나를 따라가 결함 아홉 개를 찾았습니다. 서버에서 구독을 읽는 경로 여섯 개를 하나로 모으고, 만료를 웹훅이 아니라 조회 시점에 판정하도록 바꿉니다.
- 백엔드와 운영뉴스 근사 중복 제거: 3-gram 자카드와 IDF로 해결되지 않던 것수집한 뉴스에서 같은 사건 기사를 묶는 근사 중복 제거를 만들었습니다. 문자 3-gram 자카드와 IDF 가중이 오탐을 못 잡아 선두 어절 게이트를 넣은 과정입니다.
데이터와 동기화iOS App Group SQLite 공유: 앱과 위젯이 한 DB를 쓰게 하기앱과 위젯, 공유 확장이 같은 SQLite 파일을 쓰도록 App Group으로 옮긴 기록입니다. 세션이 만료되자 앱이 다른 파일을 열어 기록이 사라져 보인 사고에서 시작합니다.
데이터와 동기화CryptoKit 종단간 암호화: libsodium 없이 키 계층 만들기libsodium을 제거하고 CryptoKit만으로 종단간 암호화 키 계층을 옮긴 기록입니다. 테스트를 쓰다가 만들어진 순간부터 열 수 없는 암호 봉투를 발견합니다.
- 데이터와 동기화CloudKit 첨부 저장소 이관: 서버가 바이트 대신 manifest만 갖기서버에 쌓던 첨부 바이트를 각 계정의 iCloud로 옮기고 서버는 manifest만 갖도록 전환했습니다. 결정 두 개를 철회하고 검증에서 결함 두 개를 고쳤습니다.
백엔드와 운영ArgoCD app-of-apps 패턴: sync-wave와 selfHeal이 못 보는 것ArgoCD 정적 app-of-apps로 클러스터 전체를 Git에서 관리하는 구조와 그 사각지대입니다. 새 노드를 붙였는데 exporter가 정상인데도 메트릭이 없던 이유를 찾습니다.
- 백엔드와 운영셀프호스팅 메일 서버: PTR 없는 발송 경로를 두 번 바꾼 기록자체 도메인으로 메일을 주고받으려다 PTR이 없어 발송 경로를 두 번 바꾼 기록입니다. SPF와 DKIM, DMARC를 넣고 수신은 Stalwart에 맡겼으며, 복구용 리스너에서 502를 만난 경위까지 적었습니다.
- 백엔드와 운영oauth2-proxy 공유 게이트: Traefik forwardAuth 인증 붙이기oauth2-proxy 하나와 Traefik forwardAuth로 내부 서비스 넷에 Google 인증을 붙였습니다. allowlist 한 줄이 네 사이트를 동시에 여는 구조와 그 이유 두 층을 다룹니다.
백엔드와 운영쿠버네티스 사이드카 readiness: 컨테이너 하나가 파드 전체를 내릴 때readiness probe가 없는 사이드카가 크래시해 파드 전체가 Service 엔드포인트에서 빠진 사건입니다. SPA는 200인데 API만 간헐 503이던 증상을 따라갑니다.
- 백엔드와 운영Nostr NIP-29 릴레이: AI 에이전트 채널과 NIP-98 인증 replay 문제Nostr NIP-29 릴레이 위에 AI 에이전트 팀을 올리며 정본과 권한을 검증한 기록입니다. 같은 초에 만든 NIP-98 인증 이벤트가 같은 id를 갖고 replay로 거부됩니다.
백엔드와 운영MCP 서버 역할별 권한: 프롬프트가 아니라 도구 등록에서 막기고객 응대 에이전트 6인에게 역할을 나누고 쓰기 권한을 MCP 도구 등록 지점에서 끊었습니다. 읽기 전용이어야 하는 두 역할에 쓰기 도구가 있던 것을 찾습니다.
백엔드와 운영App Store Connect 다국어 리뷰 응답: 언어 불일치를 호출 전에 막기App Store Connect API로 16개 언어 리뷰에 답하면서 언어 불일치를 외부 호출 전에 막았습니다. 한 나라가 두 언어에 속하고 간체와 번체가 점수로 갈리지 않는 문제를 지납니다.
시작과 구조 결정1인 개발 아키텍처 회고: 26편을 쓰고 확인한 구조와 남은 경로iPhone·iPad 유니버설 앱 하나와 쿠버네티스 네임스페이스 14개를 혼자 운영하는 구조를 정리했습니다. 26편의 원인이 대부분 같은 종류였고, 서버에서 기기로 알리는 경로만 없었습니다.
백엔드와 운영Buzz로 AI 고객센터 만들기: 에이전트 6인을 사람과 같은 방에 넣었습니다Nostr 릴레이 Buzz를 자체 클러스터에 올리고 역할이 다른 AI 에이전트 6인을 사람과 같은 프로토콜로 붙였습니다. 왜 Buzz를 골랐고 배포하면 무엇이 달라지는지 적었습니다.
시작과 구조 결정AI에게 실행을 넘기고 판정은 넘기지 않습니다iOS 앱과 Go 백엔드, 셀프호스팅 쿠버네티스를 혼자 운영하면서 AI에게 무엇을 넘겼고 무엇을 직접 붙잡았는지 적었습니다. 근거로 넘긴 파일이 이미 사실과 달랐던 일부터 시작합니다.