카테고리
IT/개발
직접 만든 앱과 서비스의 개발 기록
READY_FOR_REVIEW는 «제출됨»이 아니다: App Store Connect 제출을 멱등하게 만들기
응답이 유실되면 재실행이 중복 제출로 거부되어, 복구용 재시도가 오히려 확실한 실패가 됐습니다. 멱등 가드를 붙였더니 이번엔 그 가드가 세 가지 방향으로 배신했습니다.
UMP가 ATT를 직접 부른다: 프레임워크 바이너리로 확인한 순서 계약
AdMob IDFA 설명 메시지를 게시하려다 막혔습니다. 어느 쪽이 requestTrackingAuthorization을 부르는지 공식 문서에 없어서 UMP 프레임워크 바이너리를 읽었고, 거기서 앱과 SDK가 일회성 프롬프트를 두고 경합한다는 걸 확인했습니다.
경고 주석은 있는데 대조는 없었다: ITSAppUsesNonExemptEncryption 두 원천 검증
수출 규정 선언은 Info.plist와 제출 선언 파일 두 곳에 있습니다. 어긋나면 자동 제출이 멈추는데, 「한쪽만 고치지 말 것」이라는 경고 주석만 있고 실제로 대조하는 코드는 없었습니다.
@Suite(.serialized)로는 못 막는다: Swift Testing 병렬 실행과 전역 NotificationCenter
CI에서만 죽는 테스트 하나를 쫓다 45분 hang의 원인을 두 번 진단했습니다. 첫 진단은 hang만 고쳤고, 진짜 원인은 전역 NotificationCenter를 구독하는 뷰와 병렬 실행이 만든 격리 결함이었습니다.
이미 만료된 Task.sleep은 throw하지 않는다: 버전당 1회 리뷰 요청 지키기
AppStore.requestReview(in:)는 실패를 알려주지 않는데 게이트는 버전당 1회입니다. 같은 설계를 iOS와 Android에 옮겼더니 Swift Concurrency와 Kotlin의 취소 의미론 차이로 iOS에만 결함이 남았습니다.
Apple Pencil 손글씨 숫자 입력: PencilKit·Core ML reject 설계
PencilKit stroke를 스도쿠 숫자 입력으로 바꿀 때 필요한 tap·ink 세션 분류, 28×28 전처리, confidence reject, 비동기 추론 계약을 DailySudoku 구현으로 살펴봅니다.
픽셀보다 계약: UIKit·Jetpack Compose 스도쿠 보드 접근성
하나의 9×9 커스텀 보드를 VoiceOver와 TalkBack이 탐색할 수 있는 81개 셀로 바꾸고, 위치·값·선택·오류·입력 동작을 두 플랫폼에 매핑하는 기준을 정리합니다.
같은 날, 같은 퍼즐: generatorVersion이 있는 PuzzleIdentity 설계
같은 seed를 재현하는 것과 같은 퍼즐을 영구히 식별하는 것은 다르다. DailySudoku의 날짜·시간대·난이도·생성기 흐름을 바탕으로 generatorVersion, 마이그레이션, 롤백 계약을 정리합니다.
일회성 광고 제거 IAP: StoreKit 2·Play Billing에서 구매와 권한 분리하기
결제 완료 콜백을 영구 권한으로 착각하지 않고, 앱 시작·다른 기기·pending·환불 뒤에도 광고 제거 상태를 다시 맞추는 StoreKit 2와 Play Billing 계약을 정리합니다.
재시도 Cron은 자기 죽음을 알릴 수 없다: Watch the Watcher 설계
기록된 job ID와 live scheduler 상태를 대조하고, 작업의 존재·실행·성공·업무 진척을 분리해 사라진 재시도 작업을 찾는 최소 운영 계약을 정리합니다.