DriveRadio MEMVIA release postmortem cover
|

드라이브라디오·MEMVIA 출시 직전 사후분석: 성공한 빈 응답부터 ASC 스크린샷까지

출시 직전을 앞두고 가장 자주 반복한 질문이 하나 있었다. “이 빌드가 통과했으니 이제 올려도 되나?” 드라이브라디오와 MEMVIA를 동시에 밀면서 느낀 건, 그 질문이 너무 거칠다는 점이었다. 통과한 것은 테스트 스위트였고, 사용자가 받을 경험은 아직 따로 검증해야 했다. 이 글은 그때 따로 적어 두었던 짧은 메모 다섯 개를 한데 모은 생산 사후 분석이다. 성공한 빈 API 응답, 감상적으로 흐르는 라디오 멘트, 테스트 통과와 출시 가능의 차이, 사진 권한 없는 입력 경로, App Store Connect 스크린샷 업로드의 함정. 각각은 짧아서 공개하지 않았고, 하나로 묶어야 비로소 “왜 이런 실수를 반복했는지”가 보였다.

나는 이 기록을 홍보용으로 쓰지 않는다. 같은 함정에 다시 빠지지 않으려고 남긴다. 수치도 과장하지 않는다. 로그에서 본 것만, 코드에서 고친 것만, 아직 못 확인한 것만 적는다.

한 줄로 보면: 다섯 문제가 같은 뿌리를 공유했다

겉으로는 음악 추천, 프롬프트, 출시 체크리스트, 권한 UX, ASC API처럼 다른 영역이다. 속을 보면 공통점이 있다. 중간 단계의 성공을 최종 성공으로 착각했다는 점이다. HTTP 200이면 추천이 됐다고 생각했고, 빌드가 되면 멘트가 자연스럽다고 생각했고, 시뮬레이터 녹색이면 출시 가능이라고 말했고, AI가 들어가면 입력이 해결됐다고 봤고, 업로드 API가 응답하면 스토어 자산이 갱신됐다고 믿었다. 사용자는 그 중간 단계를 모른다. 사용자는 첫 코너에 음악이 나오는지, 멘트가 어색하지 않은지, 잠금 화면에서 제어되는지, 사진을 보내지 않아도 흐름이 사는지, 스토어에 스크린샷이 보이는지만 본다.

영역 중간 성공으로 착각한 것 사용자가 실제로 받는 결과
음악 추천 예외 없는 API 호출 재생 가능한 곡
라디오 멘트 대본 생성 완료 듣기에 담백한 문장
출시 준비 테스트·IPA 성공 실기기·차량에서 들림
MEMVIA 입력 AI 모델 연동 권한·폴백·수정 가능한 흐름
ASC 스크린샷 업로드 요청 성공 완료 상태로 스토어에 반영

1. 성공한 빈 응답: 음악이 있어야 할 자리에 폴백이 나왔다

문제

드라이브라디오는 위치 기반 이야기와 음악을 교차 편성한다. 추천 검색이 깨지면 예외와 재시도로 막으면 된다고 처음에는 생각했다. 현장 로그를 모아 보니 다른 그림이었다. 한 출근길 세션에서 검색 25회 중 7회가 오류도 타임아웃도 없이 결과 0건으로 끝났다. 결과가 있는 검색에서는 재생 후보가 정상으로 잡혔다. 즉 네트워크는 멀쩡했고, 서비스도 죽은 게 아니었다. 다만 “성공한 빈 배열”이 우리 코드에서는 성공으로 흘러갔고, 다음 단계에서 후보가 없으니 폴백 음원으로 떨어졌다. 사용자 입장에서는 음악 앱이 살아 있는데 첫 코너만 엉뚱한 소리가 나는 경험이다.

조사

예외 경로만 들여다보면 보이지 않는다. 응답 본문의 길이, 결과 건수, 그 결과가 실제로 재생 가능한지까지 한 세트로 봐야 한다. 우리는 호출 성공 여부를 로깅하고 있었고, 결과 건수는 다음 레이어에서만 보고 있었다. 그 사이 경계에서 상태가 사라졌다. 빈 배열은 예외가 아니니 catch에 안 걸리고, 후속 로직은 “후보 없음 → 폴백”으로 조용히 처리했다. 로그상으로는 성공 비율이 높게 나왔다. 귀로 들으면 실패였다.

Before / After

항목 Before After
성공 조건 예외 없음 다음 코너에 재생 가능 곡 존재
결과 0건 다음 단계로 전달 실패로 분류 후 재검색
재검색도 빈 경우 즉시 폴백 세션 내 최근 재생 가능 후보 풀 우선
폴백 사용 후보 없을 때 기본 후보 풀까지 없을 때만
계측 호출 성공/실패 결과 수, 재시도, 후보 풀 사용, 폴백 사용

백업 풀에서는 최근 재생 곡과 직전 곡을 빼서 같은 곡이 바로 반복되지 않게 했다. 완벽한 추천은 아니다. 다만 “서비스는 됐는데 귀가 이상한” 상태를 줄이는 쪽이 우선이었다.

실패 모드와 아직 볼 것

  • 구독 만료·지역 제한처럼 결과가 비는 정당한 경우와, 일시적 빈 응답을 구분하지 못하면 재검색만 늘릴 수 있다.
  • 후보 풀이 오래된 메타만 남으면 재생 불가 곡이 다시 끼어든다. 풀에 넣을 때 재생 가능 여부를 한 번 더 확인해야 한다.
  • 실제 운전 세션에서 재검색·백업 풀이 폴백 빈도를 얼마나 낮추는지, 구독 상태별로 같은지 계속 확인해야 한다.

2. 프롬프트가 다섯 겹으로 쌓이면 멘트는 감상적으로 흐른다

문제

“바람·구름·달” 같은 표현이 반복된다는 피드백을 받았을 때, 처음에는 온디바이스 모델의 한계라고 보기 쉬웠다. 대본 생성 경로를 전수 추적해 보니 원인은 모델 하나가 아니었다. 우리가 쌓아 둔 지시와 예시가 같은 방향으로 겹쳐 있었다.

조사: 무엇이 겹쳤나

  • 스타일 가이드가 감각 표현을 요구했다.
  • 예시 문장이 시적이었다.
  • 재방문 안내문도 풍경 묘사를 유도했다.
  • 편성 로직이 날씨 정보를 앞에 두었다.
  • 생성 실패 시 쓰는 폴백 템플릿도 비슷한 어조였다.

각각은 단독으로 보면 무해하다. 작은 모델에는 같은 신호가 다섯 번 들어온 셈이다. “형식만 참고하라”는 문장을 덧붙여도, 예시의 문체와 반복 지시가 더 강하게 먹혔다. 프롬프트를 고친다고 생각했는데, 실제로는 신호를 더 두껍게 쌓고 있었다.

Before / After

레이어 Before After
대본 구조 감각 훅 + 시적 전개 사실 훅 → 담백한 전개 → 다음 코너 연결
일반 이야기 코너 자연 묘사 허용 자연 묘사 금지, 날씨는 날씨 코너로 분리
예시 문장 시적·감상적 연도·인물·지명 유래 등 확인 가능한 사실
편성 순서 날씨 우선 날씨 우선 순서 뒤로
폴백 템플릿 비슷한 감상 어조 동일 기준으로 담백하게 정리
계측 인상 평가 위주 자연 클리셰 토큰 수, 대본 길이, 생성 실패 여부 (원문 미저장)

“전보다 자연스러워졌다”는 인상만으로는 다음 수정이 맞았는지 알 수 없다. 그래서 원문을 남기지 않는 선에서 반복 표현 수와 길이를 기록하도록 바꿨다. 개인정보와 대본 원문은 남기지 않는 쪽이 맞다고 판단했다.

실패 모드와 아직 볼 것

  • 예시만 바꾸면 스타일 가이드·폴백·편성이 다시 같은 방향으로 끌어당긴다. 레이어를 한 세트처럼 봐야 한다.
  • 빌드 성공은 문체 개선의 증거가 아니다. 실제 이동 중 청취에서 반복이 줄었는지는 별도 확인이 필요하다.
  • 금지 목록만 늘리면 문장이 건조해질 수 있다. 사실 훅의 품질을 같이 올려야 한다.

3. 테스트 통과 ≠ 출시 가능

문제

iPhone 17 Pro Max 시뮬레이터에서 21개 테스트가 성공했고, 서명된 배포용 IPA도 만들었다. 한국어·영어 리소스, 백그라운드 오디오·위치 권한 선언, 개인정보 매니페스트도 번들에 들어 있었다. 개발용 빌드는 실기기 설치까지 됐다. 그런데도 나는 “출시 가능”이라고 쓰지 않았다. 자동화 증거와 사용자 환경 증거는 층위가 다르다.

조사: 시뮬레이터가 증명하지 못하는 것

실기기 실행은 기기가 잠겨 있어 확인하지 못한 구간이 있었다. 운전 중 Bluetooth/AVRCP, 잠금 화면 제어, 전화·Siri 인터럽션, 터널과 네트워크 전환, 장시간 백그라운드는 시뮬레이터로 증명할 수 없다. 뉴스·지역 정보의 운영 엔드포인트와 콘텐츠 권리도 코드만으로 끝나지 않는 외부 조건이다.

감사표를 나눈 방식

영역 확인 방법 판정
경로·날씨·재생 상태 단위·통합 테스트 구현 확인
IPA 서명과 권한 선언 아카이브·번들 검사 배포 산출물 확인
실기기 오디오와 차량 제어 잠금 해제된 기기·차량 환경 별도 검증 필요
뉴스·관광 정보 운영 계약·권리·엔드포인트 외부 준비 필요

실무 원칙은 단순하다. “빌드 성공”을 출시 준비의 대명사로 쓰지 않는다. 구현됨 / 자동 테스트됨 / 기기에 설치됨 / 실제로 들림 / 외부 서비스 준비됨을 따로 적어야, 다음에도 같은 증거를 재사용할 수 있다. 이 기록은 완성 홍보가 아니라, 무엇을 아직 안 봤는지 남기는 문서다.

실패 모드와 아직 볼 것

  • 체크리스트에 “통과”만 쌓이면 미검증 항목이 가려진다. 미검증을 명시적으로 남겨야 한다.
  • 외부 콘텐츠 권리가 늦어지면 앱 코드가 준비돼도 출시 게이트가 열리지 않는다.
  • 차량·블루투스 환경은 기기·차량마다 다르다. 한 대의 성공을 일반화하지 않는다.

4. MEMVIA: 사진 권한 없이 입력을 살리는 쪽을 택했다

문제

화면 속 예약, 쿠폰, 배송, 주차, 여행 정보를 정리하는 앱을 만들 때 첫 질문은 모델 선택이 아니었다. 사용자가 어떤 경로로 데이터를 보내고, 모델을 쓸 수 없는 기기에서는 어떻게 같은 흐름을 유지할지였다. “캡처만 하면 자동 감지” 같은 말은 매력적이지만, 전체 사진 라이브러리 권한을 전제로 하면 신뢰와 심사가 먼저 흔들린다.

조사와 설계 선택

MEMVIA는 사진 선택, 붙여넣기, 공유 확장, App Intent, 지원 기기의 Visual Intelligence 연동으로 이미지·텍스트·URL을 받도록 잡았다. 핵심은 사용자가 명시적으로 보내는 흐름만 다루고, 일반 스크린샷을 백그라운드에서 감지하지 않는다는 점이다. 이미지에서는 OCR을 먼저 돌리고, 한국어·영어의 예약·쿠폰·배송·주차·여행·결제 패턴은 로컬 규칙으로 분석한다. 지원 기기에서만 Foundation Models가 구조화를 보강한다. 모델 미지원·비활성·준비 중이어도 사용자는 결과를 보고 고친 뒤 로컬 보관함에 저장할 수 있어야 했다.

Before / After (사고방식)

관점 유혹되던 Before 택한 After
권한 전체 사진 라이브러리 사용자가 고른 항목만
입력 한 경로에 집중 선택·붙여넣기·공유·Intent·VI
AI 의존 모델이 핵심 경로 OCR·규칙 기본, 모델은 보강
정확성 약속 “항상 정확” 수정 가능, 다음 행동은 확인 후
심사·데모 실사진 필수 샘플 경로로 흐름 시험 가능

일정 추가, 지도·전화·캘린더 연결 같은 다음 행동은 수정 확인 뒤에만 열었다. 인식 결과를 바로 실행하면 잘못된 일정이 캘린더에 박힌다. 그 비용은 AI 정확도 자랑보다 크다.

검증 범위와 한계

규칙·App Group 관련 단위 테스트 15개와 샘플 저장 UI 테스트 1개가 있다. 공유 확장 전달, Action Button, Visual Intelligence 진입은 지원 실기기에서 따로 봐야 한다. 공개 문서에도 “캡처만 하면 자동 감지”, “항상 정확한 AI”, “모든 iPhone 지원” 같은 표현은 쓰지 않았다. AI 기능은 화려한 진입점보다, 권한·폴백·수정 가능성·실기기 검증을 포함한 전체 입력 흐름으로 설계해야 한다.

5. ASC 스크린샷 업로드: API 응답을 믿기 전에 확인할 것

문제

스크린샷을 만들었다고 작업이 끝나지 않았다. 업로드 API의 화면 분류값, 파일 예약·전송·완료, 빈 응답을 각각 확인해야 스토어 자산이 실제로 바뀌었다. 자동화의 가치는 버튼 클릭을 없애는 데 있지 않다. 입력값·단계별 응답·최종 상태를 다시 읽어, 같은 실수를 반복하지 않게 만드는 데 있다.

배운 네 가지

이름과 실제 API 값은 다를 수 있다. 6.9인치용 이미지를 올릴 때 직관적으로 예상한 화면 타입 값은 없었다. API가 돌려준 허용 enum을 먼저 확인하고, 기존 iPhone 6.7인치 세트가 실제 규격을 받는지 예약·업로드·완료까지 검증했다. 문서나 UI 명칭만 보고 분류값을 가정하면 작업이 멈춘다.

업로드는 한 번의 POST가 아니었다. 예약 생성, 바이너리 PUT, 완료 PATCH의 세 단계였다. 체크섬을 처음 요청에 넣으면 오류가 났고, 전송이 끝난 뒤 완료 요청에 넣어야 했다. 각 단계 상태를 안 보면 목록에 이미지가 보여도 처리 중이거나 실패한 채로 남을 수 있다.

삭제 응답은 비어 있을 수 있다. 기존 스크린샷을 교체할 때 삭제 API는 204와 빈 본문을 반환했다. 모든 응답을 JSON으로 가정한 공통 함수가 여기서 깨졌다. 상태 코드와 본문 존재 여부를 분리한 뒤, 삭제 → 업로드 순서로 교체했다. 세트의 최대 장수 제한도 먼저 봐야 한다.

이런 단계별 응답은 스크립트 로그만 봐서는 놓치기 쉽다. 터미널에서 돌리는 스크립트가 실제로 어떤 요청을 보내고 무엇을 돌려받는지 하나씩 열어 보는 방법은 HTTP Toolkit 사용법에 따로 적어 두었다.

최종 판정은 API 호출 성공이 아니다. 새 이미지의 완료 상태를 다시 읽었다. 그래도 스크린샷 업로드가 앱 출시를 보장하지는 않는다. 실기기·차량 검증, TestFlight, 콘텐츠 권리, 개인정보 응답처럼 별도 게이트가 남아 있다.

실무 체크리스트

단계 확인할 것 흔히 놓치는 점
분류값 API 허용 enum UI 명칭과 enum 불일치
예약 업로드 URL·만료 체크섬을 너무 이르게 전송
PUT 바이너리 전송 완료 목록 노출을 완료로 오인
PATCH 완료 상태 재조회 한 번 성공 응답만 보고 종료
삭제 204·빈 본문 허용 JSON 파서로 실패 처리
세트 최대 장수 초과 후 원인 불명 오류

다섯 사례를 가로지르는 운영 원칙

나는 이 작업을 하면서 체크리스트를 더 길게 만들기보다, 판정 단위를 바꿨다. 중간 계층의 성공이 아니라 사용자가 만지는 최종 산출물을 성공으로 둔다. 그 기준이면 빈 배열은 실패고, 시적 멘트는 실패고, 시뮬레이터 녹색은 출시가 아니며, AI 연동만으로는 입력이 완성되지 않고, 업로드 200만으로는 스토어가 갱신되지 않는다.

  • 상태 계약을 사용자 결과로 쓴다. 예외 유무가 아니라 재생 가능 곡, 담백한 멘트, 잠금 화면 제어, 수정 가능한 인식 결과, 완료된 스토어 자산.
  • 겹친 신호를 한 세트처럼 본다. 프롬프트 다섯 겹, ASC 세 단계, 출시 게이트 여러 층은 각각 따로 고치면 다시 어긋난다.
  • 미검증을 문서에 남긴다. 확인한 것과 못 한 것을 같은 표에 두면, “빌드 성공”이 과대해석되지 않는다.
  • 계측은 원문·개인정보 없이. 반복 토큰 수, 결과 건수, 폴백 사용 여부처럼 의사결정에 필요한 숫자만.
  • 공개 문구는 검증 범위 안에. “항상”, “모든 기기”, “자동 감지”는 검증이 따라오지 않으면 쓰지 않는다.

같은 원칙을 바이브 코딩으로 만든 웹앱에 옮기면 질문이 조금 달라진다. AI가 짠 코드가 실제 사용자 앞에서 어디부터 무너지는지는 바이브 코딩 앱 프로덕션 가이드에, 테스트가 녹색이어도 데이터 권한이 뚫려 있을 수 있는 지점은 Supabase RLS 설정 가이드에 따로 정리했다.

출시 전에 다시 볼 통합 체크리스트

게이트 질문 통과 기준(예시)
콘텐츠 경로 빈 성공 응답이 폴백으로 새는가? 결과 0건 → 재검색 → 후보 풀 → 폴백 순서와 로그
생성 품질 지시·예시·폴백이 같은 방향으로 겹치는가? 레이어 전수 점검 + 클리셰 계측
자동 테스트 무엇이 증명됐고 무엇이 아닌가? 구현/배포/실기기/외부를 표로 분리
실기기 잠금 화면·블루투스·인터럽션에서 동작하는가? 잠금 해제 기기·차량 환경 기록
입력·권한 권한 거부·모델 부재 시에도 흐름이 사는가? 폴백 경로와 샘플 경로 확인
스토어 자산 업로드 후 완료 상태를 재조회했는가? enum·3단계·204·장수 제한 확인
외부 조건 권리·엔드포인트·개인정보 응답이 준비됐는가? 코드 밖 항목을 출시 차단 조건으로 명시

이 글이 다루지 않는 것

특정 음악 API나 ASC의 성능을 일반화하지 않는다. 한 팀의 iOS 앱 두 개에서 본 현장 로그와 수정 기록이다. 출시가 끝났다는 선언도 아니다. 차량 환경, 장기 청취 품질, Visual Intelligence 실기기 진입, 콘텐츠 권리처럼 아직 표에 “별도 검증”으로 남은 항목이 있다. 그 항목을 가리지 않는 것이 이 사후 분석의 목적이다.

짧은 메모 다섯 개를 공개하지 않은 이유도 같다. 조각만 보면 “API 팁”이나 “프롬프트 팁”으로 읽히고, 중간 성공을 최종 성공으로 착각한 패턴이 안 보인다. 하나로 묶어야 다음 출시에서 같은 질문을 다시 던질 수 있다. 통과한 것은 무엇인가. 사용자가 받을 결과는 무엇인가. 둘을 같은 문장에 쓰지 말 것.