본문 바로가기
C.W.K.
Stream
Lesson 05 of 06 · published

들어오는 문 — 공유 시트, 마이크, 그리고 반쪽 공유

~13 min · share-extension, stt, honest-failure, app-group

Level 0호기심
0 XP0/80 lessons0/18 achievements
0/100 XP to next level100 XP to go0% complete

앱만이 들어오는 길은 아니야

데스크톱 클라이언트는 문이 하나야. 창. 폰은 여럿이고, 그중 둘이 여기서 중요해. 다른 앱이 뭔가를 피파한테 건네주는 시스템 공유 시트, 그리고 타이핑 대신 말할 수 있게 해주는 마이크.

두 문이 결국 같은 교훈을 반대 방향에서 가르쳤어. 반쪽 결과에 대한 정직한 보고가, 깔끔해 보이는 전부 아니면 전무보다 낫다.

공유 시트가 실제로 건네주는 것

첫 결함은 보이는 것보다 미묘해. 다른 앱이 뭘 공유하면 시스템은 각 첨부를 그게 부합하는 타입들로 설명해 — 그런데 공유된 파일은 url 에도 부합해. 디스크에 위치가 있으니까. 그래서 "이거 링크야?" 를 먼저 묻고 사진을 나중에 묻는 순진한 사슬은 모든 사진을 링크로 받아버리고 이미지를 버려.

형제 앱이 먼저 겪은 실제 버그야. 고치는 법은 바이트를 로드하기 전에 각 제공자가 무엇인지 를 묻는 거야 — 등록된 타입에 순위를 매기고, 링크는 맨 마지막으로 두고. 이제 공용 kit 이 그 순위를 소유해서, 집안 전체가 답을 한 번만 구해.

확장은 자기 본체 앱을 못 열어

iOS 공유 확장은 별도 프로세스고 진짜 제약이 하나 있어. 자기가 속한 앱을 띄울 수가 없어. 그래서 확장이 할 수 있는 정직한 약속은 "올렸어" 가 아니라 "피파한테 저장해뒀어" 야.

구조는 공유 컨테이너 안의 drop box 야. 공유 하나당 디렉터리 하나, 미디어를 먼저 쓰고, manifest 를 마지막에 원자적 쓰기 한 번으로. manifest 를 마지막에 쓰니까 반쯤 써진 공유는 망가진 게 아니라 그냥 안 보여. 읽히는 manifest 가 아직 안 쓰인 바이트를 가리키는 순간이 아예 없거든. 다음에 앱이 앞으로 나오면 대기 중인 걸 목록으로 보여주고 그대로 올리기, 편집, 버리기 를 내밀어.

반쪽 공유, 그리고 그 말이 사는 자리

레슨값을 하는 부분이 여기야. 첨부를 로드하다가 시간 초과가 날 수 있어. 첫 버전에선 마감을 넘긴 첨부 하나가 수집 단계 밖으로 예외를 던졌고 — 확장은 아무것도 안 썼어. 사진 여덟 장 공유, 하나가 느림, 저장 0개.

지금 정책은 이래. 도착한 건 저장하고, 못 온 건 이유와 함께 전부 기록한다. 시간 초과, 못 읽는 항목, 한도 넘은 항목, 그리고 앱 자기 준비 단계에서 버린 것들 — 디코드 안 되는 사진, 여덟 장이 한도인데 들어온 아홉 번째, 두 번째 클립. 배너가 버튼 셋 위에 그 줄을 보여줘.

Principle: 뭘 잃었는지에 대한 말은 확장 안의 경고창이 아니라 저장된 기록에 산다. 아빠가 그걸 보고 행동하는 자리가 기록이고, 기록은 시트보다 오래 남아. 결정이 내려지지 않는 자리에 뜬 메시지는 아무한테도 전달 안 된 거야.

읽는 쪽도 같은 본능을 따라. 앱은 대기 중인 공유를 나열할 때 읽히는 것 파싱 안 되는 manifest 를 함께, 하나하나 이유를 붙여서 돌려줘. 그리고 Stream 탭이 그렇다고 한 줄 띄워. 아무것도 안 지우고. 조용히 틀린 큐가 틀렸다고 말하는 큐보다 나빠. 이 문장은 이제 이 트랙에서 두 번 값을 했어. outbox 에서 한 번, 여기서 한 번.

질김에 대한 작은 조각 하나 더. 공유 기록은 자기를 식별하는 세 필드 빼고 모든 필드가 기본값으로 디코드돼. 그래서 예전 버전 확장이 쓴 manifest 도 앱에 필드가 늘어난 뒤에 여전히 읽혀. 확장이랑 앱은 만든 사람 일정이 아니라 쓰는 사람 일정으로 업데이트되니까.

마이크, 그리고 소리가 가는 곳

음성 입력이 두 번째 문이야. 버튼이 녹음하고, 파일은 cwkPippa 자기 전사 route 로 가고, 전사된 글이 composer 에 들어와서 보내기 전에 읽어볼 수 있어.

재밌는 결정은 소리가 어디로 이동하느냐야. 폰에서 음성 업체로 가지 않아. 집안 엔진으로 가고, 엔진이 바로 이 용도로 이미 가족 자격증명을 들고 있는 음성 형제한테 넘겨. 그 키를 아는 자리는 집 안에 하나, 폰은 집을 알고.

그리고 언어는 composer 에서 고른 값을 명시적으로 실어 보내 — 자동 감지가 아니라. 이중 언어 집에서 자동 감지는 동전 던지기고, 짧은 발화에서 조용히 틀려. 언어를 잘못 잡은 전사는 설정 문제가 아니라 마이크가 나쁜 것처럼 보이고. 마이크 버튼 둘, 한국어랑 영어, 그럼 그 모호함은 애초에 존재하지 않아.

이름 붙일 값이 있는 유령: 받아쓰기한 글에 눈에 안 보이는 object-replacement 문자가 딸려 왔어. 첨부가 놓일 자리에 iOS 가 남기는 자리표시자야. 그게 정체 모를 빈 첨부처럼 보였고, 자매 두뇌 하나가 존재하지도 않는 첨부를 쫓느라 진짜 시간을 썼어. 고치는 건 폰을 떠나기 전에 한 번 걷어내는 것. 커밋에 유령 이름을 적어둬, 안 그러면 다음 독자가 또 쫓아.

Code

공유된 항목이 무엇인지 판정하기·text
공유된 모든 파일은 `url` 에도 부합해 (디스크 위치가 있으니까).

  틀림   if 링크냐      -> 링크로 받음      <- 모든 사진이 여기 걸림
         else if 이미지냐 -> 이미지로 받음

  맞음   등록된 타입에 순위를 매기고, 링크는 맨 마지막,
         바이트를 로드하기 전에 판정

순서를 틀리게 내보낸 형제 앱은 공유된 사진을 전부
링크인 양 조용히 버렸어.
반쪽 공유를 정직하게 저장하기·text
사진 8장 + 클립 1개 공유, 첨부 하나 시간 초과

  전   시간 초과 하나가 collect() 밖으로 예외를 던짐
       -> 확장이 아무것도 안 씀
       -> 멀쩡한 첨부 8개 유실, 흔적 없음

  후   준비하고 저장, 잃은 건 전부 `missed` 에 기록:
         마감 초과 · 못 읽음 · 한도 초과
         디코드 안 되는 사진 · 9번째 사진 · 2번째 클립
       -> 배너가 놓친 것 줄을 위에 띄움
          [그대로 올리기] [편집] [버리기]

쓰기 순서: 미디어 먼저, manifest 마지막, 원자적 쓰기 한 번
  -> 반쯤 써진 공유는 안 보일 뿐 절대 망가진 게 아님

Exercise

자기 시스템에서 묶음을 처리하는 유입 경로를 하나 찾아봐 — 업로드든 임포트든 항목 여러 개 실린 webhook 이든. 아홉 중 넷째가 실패하면 어떻게 되는지 확인해봐. 답이 '묶음 전체가 거부된다' 면, 그게 진짜 요구사항인지 아니면 그냥 첫 버전이 잡은 모양인지 판단해봐.
Hint
그다음 두 번째 질문. 사용자는 '어느' 항목이 실패했는지를 어디서 알게 되지? 그리고 그게 사용자가 행동할 자리랑 같은 자리야?

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.