왜 우리 앱 아이콘은 얇아 보였을까 — Fellow를 뜯어보고 메탈로 다시 그린 하루

교집합 두 원이라는 컨셉은 좋았는데 고급스러움이 안 났다. Fellow 커피 아이콘과 나란히 놓고 차이를 분해한 뒤, AI와 함께 메탈릭 링으로 다시 그리고, 같은 날 OTA 부팅 크래시까지 잡은 기록.

왜 우리 앱 아이콘은 얇아 보였을까 — Fellow를 뜯어보고 메탈로 다시 그린 하루

앱 아이콘 하나 바꾸는 데 하루를 썼다. 정확히는, “왜 우리 아이콘은 싸 보이지?”라는 찜찜함의 정체를 알아내는 데 하루를 썼다.

프라이밋(Primeet) 아이콘은 두 개의 원이 겹친 교집합 모양이다. ‘서로 다른 두 사람이 섞인다’는 뜻. 컨셉은 마음에 들었다. 그런데 홈 화면에 깔아두면 어딘가 빈약했다. 옆에 있는 Fellow(커피 기구 브랜드) 앱은 비슷하게 미니멀한데도 묵직하고 고급져 보이는데, 우리 건 자꾸 도망갔다. 같은 ‘동그라미 두 개’인데 뭐가 다른 걸까?

책상 위에 나란히 놓인 두 개의 미니멀 앱 아이콘 — 하나는 얇은 선, 하나는 묵직한 메탈

차이는 ‘선’이 아니라 ‘질량’이었다

AI한테 두 아이콘을 나란히 분해시켰다. 결론이 명쾌했다.

항목기존 우리 아이콘프리미엄 마크(Fellow 등)
선 두께캔버스 대비 약 1.2%8~14%
형태빈 윤곽선(stroke)채운 면 또는 두꺼운 링
핵심부교집합이 그냥 비어 있음강조 처리로 컨셉이 읽힘
60px 렌더선이 0.7px → 사실상 소멸작아져도 실루엣 유지

핵심은 두께가 아니라 질량감이다. 헤어라인 같은 얇은 선은 종이에 연필로 그린 스케치처럼 보이고, 두꺼운 링이나 채운 면은 금속 부품처럼 보인다. 우리 마크는 1.2% 두께라 64px 앱 아이콘에서 선이 1px도 안 되게 렌더돼 사라지고 있었다. 작은 화면에서 실루엣이 안 남으면, 뇌는 그걸 ‘저렴하다’고 읽는다.

게다가 ‘섞임’의 핵심인 교집합(두 원이 겹치는 렌즈 모양)이 아무 처리 없이 비어 있었다. 컨셉의 주인공을 빈칸으로 둔 셈이다.

골드 → 실버, 그리고 “흰 번짐 좀 지워줘”

방향을 잡고 시안을 뽑기 시작했다. 첫 라운드는 골드 메탈릭. 두께를 6~9%로 올리고, 금속 그라디언트와 드롭섀도를 넣었다. 확실히 묵직해졌는데 — 피드백은 한 줄이었다.

“골드 너무 올드하다.”

맞는 말이다. 골드 그라디언트는 자칫 2010년대 ‘럭셔리’ 클리셰로 빠진다. 기존 블랙&화이트 정체성을 유지하되, 화이트라기보단 ‘메탈’ 느낌, 그리고 좀 더 얇게. Fellow가 고급져 보이는 건 화려해서가 아니라 무광 금속 질감 때문이라는 걸 그제야 정리했다.

골드에서 실버 스틸로 넘어가는 아이콘 시안 비교 시트

실버/플래티넘/다크 스틸 3종을 뽑았다. 스틸 그라디언트(#F2F3F5 → #6E707A)로 위에서 빛 받고 아래로 떨어지는 금속 면을 흉내 냈다. 그런데 또 한 줄이 날아왔다.

“좌측 위에 미세하게 흰 그림자 있네? 지저분해.”

내가 메탈 광택을 살리려고 링 상단에 블러 처리한 하이라이트 아크를 얹었는데, 작은 사이즈에서 그게 번진 얼룩처럼 보인 거다. 빼버렸다. 어차피 그라디언트 자체가 좌상단 밝음 → 우하단 어두움으로 금속감을 만들고 있어서, 하이라이트 아크는 사족이었다. 덜어내니 더 깨끗해졌다. 디자인에서 자주 나오는 교훈인데 매번 까먹는다.

최종 채택은 다크 스틸, stroke 4.3%, 드롭섀도 + 희미한 렌즈 틴트. 얇되 헤어라인은 아니고, 금속이되 번쩍이지 않는 선.

한 SVG에서 아이콘 전체를 굽는다

확정 후엔 노가다가 아니라 스크립트다. 마크를 SVG 하나로 정의하고, sharp-cli로 필요한 사이즈를 전부 굽는 _gen.sh를 레포에 넣었다.

1
2
3
4
5
6
7
# 1024 캔버스, 교집합 링: cx 376/648, r 272, stroke 44 (≈4.3%)
# icon.png        — 풀스퀘어 (iOS가 모서리 라운딩)
# adaptive-icon   — 안드로이드 마스크 안전영역(중앙 66%)에 맞춰 0.66 축소
# splash / favicon — 동일 마크, 사이즈만 변경
for f in icon adaptive-icon splash splash-icon favicon; do
  npx sharp-cli -i $f.svg -o ../$f.png
done

이렇게 해두면 다음에 색이나 두께를 바꿔도 _gen.sh 한 번이면 끝이다. 손으로 PNG 5장을 다시 export하지 않는다. 원본(SVG)과 산출물(PNG)을 코드로 묶어두는 것 — 이게 디자인 자산을 코드처럼 다루는 핵심이다.

여기서 배경색도 통일했다. 웹은 흰 배경, 앱 스플래시는 다크라서 들어갈 때마다 화면이 번쩍 바뀌는 이질감이 있었는데, 전부 브랜드 다크 #1C1C24로 맞추고 웹/앱 공통 로딩 스플래시(다크 배경 + 같은 마크)를 깔았다. 이제 웹에서 켜든 앱에서 켜든 같은 화면으로 시작한다.

다크 배경 위 메탈릭 교집합 링 — 최종 아이콘이 홈 화면에 놓인 모습

보너스: 같은 날 잡은 OTA 부팅 크래시

아이콘 작업을 하려면 어차피 네이티브 빌드(아이콘 변경은 OTA로 안 됨, TestFlight 재빌드 필요)를 올려야 했다. 그래서 묵혀둔 크래시도 같이 잡았다.

테스터한테서 “errorrr”, “why again?” 두 줄짜리 피드백과 함께 크래시 로그가 왔다. 스택을 까보니:

1
2
3
4
5
ErrorRecovery.crash()                    ← expo-updates
ErrorRecovery.waitForRemoteLoaderToFinish()
ErrorRecovery.startPipeline(withEncounteredError:)
hermes::vm::stringPrototypeSplit         ← JS 스레드
→ SIGABRT

범인은 expo-updates의 ErrorRecovery였다. OTA(Over-The-Air, 앱스토어 재심사 없이 JS 번들만 교체하는 업데이트)로 갓 받은 번들이 부팅 직후 잡히지 않은 JS 에러를 던지자, expo가 “복구해볼게” 하고 원격에서 더 새 번들을 받으려다 실패하고, 결국 원래 에러를 다시 던지며 앱을 죽인 거다.

문제를 키운 건 우리 부팅 코드였다. 앱을 켤 때마다 이걸 동기적으로 하고 있었다.

1
2
3
4
checkForUpdateAsync()
  → '업데이트 중' 화면 표시
  → fetchUpdateAsync()
  → reloadAsync()   // 받자마자 강제 재시작

갓 받은 번들에 startup 문제가 있으면 → 그 자리에서 크래시 → 다음에 켜도 또 받고 또 크래시. “why again?”의 정체다. 게다가 잘 동작할 때도 켤 때마다 “업데이트 중…” 뜨고 앱이 한 번 꺼졌다 켜지니, 사용자 입장에선 업데이트가 안 된 것 같고 앱이 자꾸 죽는 것처럼 느껴진다.

고친 방향은 단순하다. 비차단(non-blocking)으로 바꿨다.

1
2
3
4
5
// 부팅 시 reloadAsync 안 함. 현재(검증된) 번들로 즉시 시작하고,
// 새 번들은 백그라운드로 받아 '다음 cold start'에 자연스럽게 적용.
Updates.checkForUpdateAsync()
  .then((c) => (c.isAvailable ? Updates.fetchUpdateAsync() : null))
  .catch(() => { /* 실패해도 현재 버전으로 계속 */ });

이러면 (1) 앱이 켤 때마다 안 꺼지고, (2) 새 번들이 부팅에서 깨져도 expo의 embedded 자동 롤백이 정상 작동할 시간을 번다. ‘업데이트 중’ 화면과 강제 재시작이라는 가장 거슬리던 UX가 통째로 사라졌다.

그래서, 하루 쓸 만했나

앱 아이콘은 사용자가 하루에도 수십 번 보는 가장 작은 광고판이다. 그게 64px에서 사라지고 있었다면, 그건 디테일이 아니라 매일 새는 첫인상이다. 차이를 ‘선이 얇다’가 아니라 ‘질량감이 없다’로 정의한 순간 방향이 잡혔고, 골드의 올드함과 하이라이트의 얼룩을 한 번씩 밟고 나서야 깨끗한 답이 나왔다.

작업 과정은 전부 primeet-pr 레포에 메이커스로그로 남겼다. 시안 5장, 의사결정 한 줄 한 줄, 최종 SVG까지. 다음에 또 “왜 이렇게 만들었더라” 할 때 펴볼 수 있게.

덜어내니 더 깨끗해졌다. 디자인이든 부팅 코드든.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.