🐝매일 한입
개발일지📖 14분 읽기

Oftway 1.0 심사 제출 — 로그인 버튼 셋을 같은 크기로, 카카오 키는 장소용과 로그인용으로

Oftway 1.0 을 App Store 심사에 내기 전 하루 동안 한 일. 높이가 달랐던 Apple·카카오·Google 버튼을 같은 규격으로 맞추고, Apple 로그인에서는 이름을 묻지 않고, 카카오 장소 키는 무료 쿼터 때문에 그대로 두되 로그인은 따로 만든 앱으로 나눈 이유.

📖 14분 읽기
#Oftway#App Store 심사#Sign in with Apple#카카오 로그인#Expo#1인개발

0.1.0 에서 1.0.0 으로

Oftway 는 그동안 TestFlight 로 저 혼자 쓰던 앱이었습니다. 10월 22일, 이제 스토어에 낼 때가 됐다고 정하고 store-release 라는 intent 를 열었습니다. 그날 적은 Problem 을 보면 낼 준비가 거의 안 되어 있었습니다. 앱 버전은 0.1.0 이었고 production 빌드를 만든 적이 없었습니다. 지금까지는 실험 화면이 켜진 preview·testflight 빌드뿐이었습니다. 10월 7일에 써 둔 등록 문안은 그 뒤에 생긴 리포트 밤 표지, 발자취 색, 시간 흐름 재생, 방향 화살표를 모르고 있었고, 6.9인치 스크린샷은 한 장도 없었습니다.

로그인도 걸렸습니다. Oftway 의 로그인은 선택 기능이라 하지 않아도 무료 기능과 Pro 구매를 모두 쓸 수 있습니다. 켜져 있던 것은 Apple 하나였는데, 1.0 에 카카오와 Google 까지 넣기로 그날 정했습니다. 이 글은 그 로그인 쪽 이야기입니다.

높이가 다른 버튼 세 개

TestFlight 1.0.0 (6) 빌드의 설정 › 계정 화면을 보니 버튼 셋의 높이와 글자가 서로 달랐습니다. Apple 은 시스템 버튼이라 44pt 에 시스템 글꼴이었고, 직접 그린 카카오·Google 버튼은 실제로 약 37pt 로 낮게 나왔습니다. 카카오 말풍선과 Google G 아이콘도 없었습니다. account-profile intent 에 이걸 심사 전에 바로잡아야 한다고 적은 이유는 Joiny 입니다. Joiny 가 1차 심사에서 거절된 근거가 로그인 버튼이 동등하게 보이는지였습니다.

그래서 세 버튼을 같은 규격 하나로 묶었습니다. 높이 48, 모서리 12, 같은 글자 스타일, 아이콘 + 문구. 색만 각 회사 규정을 따릅니다.

/**
 * 로그인 버튼(account-profile spec §1). 세 수단 모두 높이 48·모서리 12·같은 글자(`headline`)·아이콘 + 문구로 같게 둔다 —
 * Apple 버튼이 다른 버튼보다 작거나 덜 눈에 띄면 안 된다(account spec §1, Joiny 1차 거절). 시스템 Apple 버튼은 글꼴·크기를
 * 바꿀 수 없어 규정(Apple 로고 + 검정/흰 바탕)대로 직접 그린다(Joiny와 같다). 색은 각 사 규정을 따른다(DESIGN §3).
 */
const HEIGHT = 48;
const RADIUS = 12;

const BRAND = {
  kakao: { background: '#FEE500', label: 'rgba(0, 0, 0, 0.85)' },
  google: { background: '#FFFFFF', border: '#747775', label: '#1F1F1F' },
} as const;

가장 큰 결정은 Apple 버튼을 시스템 버튼에서 직접 그린 버튼으로 바꾼 것입니다. 시스템 버튼은 글꼴도 크기도 바꿀 수 없습니다. 규정대로 Apple 로고와 검정/흰 바탕으로 직접 그린 버튼이 Joiny 에서 이미 심사를 통과했기에 그 길을 따랐습니다. Apple 은 맨 위에 둡니다.

같은 커밋에서 로그인한 뒤의 모습도 바꿨습니다. 전에는 "카카오로 로그인함" 한 줄만 보여서, 계정을 여럿 쓰는 사람은 지금 어느 계정인지 알 수 없었습니다. 이제는 계정 묶음 맨 위에 사진·이름·이메일 카드가 있고, 사진 오른쪽 아래에 로그인 수단 표시가 붙습니다. 세션에서 프로필을 꺼내는 부분은 순수 함수로 뺐습니다.

export function accountProfile(user: UserLike | null | undefined): AccountProfile | null {
  if (!user) return null;
  const meta = user.user_metadata ?? {};
  const email = (typeof user.email === 'string' && user.email.trim()) || first(meta, ['email']);
  const avatar = first(meta, ['avatar_url', 'picture']);
  return {
    name: first(meta, ['full_name', 'name', 'nickname', 'preferred_username']),
    email: email || null,
    hiddenEmail: Boolean(email && RELAY.test(email)),
    // iOS ATS가 http를 막는다(카카오 사진 주소가 http로 올 때가 있다).
    avatar: avatar ? avatar.replace(/^http:\/\//i, 'https://') : null,
  };
}

제공자마다 이름이 들어 있는 키가 달라 네 개를 차례로 봅니다. Apple 의 이메일 가리기 주소는 주소 대신 "Apple 이메일 가리기 사용 중" 이라는 문구로 보입니다. 이 프로필은 화면에만 쓰고 분석·오류 보고에 보내지 않습니다.

Apple 에서는 이름을 묻지 않는다

카드를 만들고 나면 바로 걸리는 데가 있습니다. Apple 로그인은 이름도 이메일도 주지 않으니 카드가 "Apple 계정" 이라는 대체 문구로만 보입니다. 이름을 받으려면 로그인할 때 범위를 요청하면 됩니다. intent 의 Open questions 에서 저는 그러지 않기로 했습니다. Joiny 2차 거절이 근거입니다. 제약에는 "Apple 로그인은 이름·이메일을 요청하지 않고 로그인 뒤 아무것도 묻지 않는다" 고 적혀 있고, 이름 바꾸기·사진 올리기도 이번 범위 밖으로 뺐습니다. 카드가 조금 덜 채워지는 쪽을 택한 것입니다.

카카오도 비슷합니다. 이메일은 비즈 앱으로 바꾸지 않으면 받을 수 없어 받지 않았고, 닉네임과 프로필 사진만 선택 동의로 두었습니다. 그래서 이메일 자리에는 "카카오로 로그인했어요" 같은 문구가 들어갑니다.

심사 메모도 이 결정에 맞춰 고쳤습니다. 로그인이 어떤 기능에도 필요 없으니 데모 계정도 필요 없다는 것을 한 줄로 밝혔습니다.

Sign-in is optional (Settings > Account: Sign in with Apple, Kakao, Google). It is not needed for
any feature, including Pro, so no demo account is needed. The account holds only an ID, provider,
sign-up time and the email/name the provider shares (Sign in with Apple requests no scopes), and can
be deleted in the app (Settings > Account > Delete account).

카카오 키를 둘로 나눈 이유

카카오는 Oftway 에서 두 가지 일을 합니다. 하나는 한국 안에서 머문 곳에 이름을 붙일 때 장소 후보를 받아 오는 카카오 로컬이고(ADR-0016), 다른 하나는 이번에 들어온 로그인입니다. ADR-0016 원래 계획은 테스트 기간에는 Dayseed 앱의 키를 쓰다가 출시 때 Oftway 전용 앱 + 비즈월렛 + 하루 사용 상한으로 옮기는 것이었습니다.

출시 당일에 그 계획을 고쳤습니다. 2026년부터 카카오맵 무료 쿼터는 계정의 첫 카카오맵 앱에만 주어집니다. 전용 앱을 새로 만들면 처음부터 비즈월렛 요금이 붙습니다. 그래서 장소 후보용 REST 키는 출시 뒤에도 Dayseed 앱 키를 그대로 쓰고, 두 앱이 하루 쿼터를 나눠 쓰다가 모자라면 그때 옮기기로 했습니다.

로그인은 반대로 했습니다. 카카오 디벨로퍼스에 Oftway 앱을 따로 만들어 로그인 전용으로 두었습니다. ADR 에 남긴 이유는 "동의 화면 이름과 사용자 ID·탈퇴 연결 끊기가 Dayseed 와 섞이지 않게" 입니다. 장소 검색은 사용자 눈에 보이지 않는 요청이지만, 로그인은 동의 화면에 앱 이름이 뜨고 사용자 ID 가 앱에 묶입니다. 비용 때문에 공유해도 되는 것과 공유하면 안 되는 것을 그 기준으로 갈랐습니다.

- **키**: REST 키는 앱에 들어간다(노출). 카카오맵 무료 쿼터가 있는 Dayseed 앱의 키를 출시 뒤에도 쓴다
  (2026년부터 무료 쿼터는 계정의 첫 카카오맵 앱에만 준다 — 전용 앱은 처음부터 비즈월렛 요금).
  두 앱이 하루 쿼터를 나눠 쓰니, 모자라면 그때 Oftway 전용 앱 + 비즈월렛 + 하루 상한으로 옮긴다.
  카카오 **로그인**은 따로 만든 Oftway 앱으로 한다 — 동의 화면 이름과 사용자 ID·탈퇴 연결 끊기가 Dayseed와 섞이지 않게.

Google 은 OAuth 클라이언트를 iOS·웹 두 개 만들었고, 앱에 들어가는 공개 값은 EAS 환경 변수에 넣었습니다. 이 값이 없으면 그 로그인 버튼이 숨는다는 줄을 등록 문안의 출시 전 확인 목록에 더했습니다.

확인한 것과 남은 것

plan 의 진행 기록으로는 10월 22일 하루에 외부 설정, 처리방침 공개(계정 이름·프로필 사진 포함), 개인정보 라벨(이메일·이름·사용자 ID, 추적 아님), 6.9인치 스크린샷 7장과 빌드 1.0.0 (6) 입력까지 마치고 심사 제출은 제가 직접 눌렀습니다.

막힌 데는 하나였습니다. 첫 비소모성 상품인 Pro 를 1.0 제출에 묶어야 하는데, 상품 상태가 "제출 준비 중" 이라 버전 페이지에 칸이 생기지 않았습니다. 내부 상태는 MISSING_METADATA 로 남았지만 상품 페이지의 "심사에 추가" 로 iOS 1.0 과 같은 제출 초안에 들어갔고 오류가 없었다고 기록했습니다. 첫 비소모성 상품은 버전 페이지 칸이 아니라 이 방식으로 묶인다는 것이 그날 배운 것입니다.

안 한 것도 있습니다. 제품 문서의 심사 대응 원칙은 심사 메모에 실제로 걸으며 기록되는 시연 영상을 넣는 것이었는데, 이번에는 내지 않았습니다. 꼭 필요한 게 아니면 건너뛰고, 심사에서 요청하면 그때 찍어 내기로 했습니다.

빈칸도 적어 둡니다. account-profile plan 의 마지막 항목은 lint·typecheck·test 와 실험 시뮬레이터에서 카드 세 경우(이름+이메일+사진, 이름만, 아무것도 없는 Apple)를 라이트·다크로 보는 것인데, 그 결과는 레포에 적혀 있지 않습니다. store-release plan 의 "개발 빌드에서 세 로그인 확인" 도 결과 기록이 없고, 카카오 id_token 에 닉네임·사진이 실제로 들어오는지는 실기기에서 확인하겠다는 Open question 으로 남아 있습니다. 그리고 이때 낸 1.0.0 (6) 빌드는 사흘 뒤 잠긴 폰에서 깨어날 때 죽는 문제가 드러난 그 빌드입니다. 심사 결과와 그 뒤 경위는 아직 레포에 기록되어 있지 않습니다.

Oftway 는 따로 기록하지 않아도 다닌 길이 지도에 남고, 자주 다닌 길일수록 짙어지는 발자취 일기입니다. 위치는 폰 밖으로 나가지 않고, 로그인은 하지 않아도 됩니다.

📚 관련 글

💬 댓글