앱을 열기 전에도 이어져야 하는 iOS 경험

위젯, 알림, 딥링크, 로컬 캐시가 같은 공원 상태를 보게 만든 기록.

iOS 앱을 만들면서 가장 먼저 깨달은 것은, 앱이 아이콘 안에서만 쓰이지 않는다는 점이었다.

처음에는 앱 화면 하나와 위젯 하나면 충분할 줄 알았다. 직접 써보려 하니 iOS 쪽에서 해야 할 일이 빠르게 늘었다.

특히 출발 직전의 사용자는 차분히 앱을 탐색하지 않는다. 집에서 나가기 직전이거나, 엘리베이터 앞이거나, 이미 차에 앉아 있을 때가 많다. 그 짧은 순간에 앱, 위젯, 알림, 지도 앱 이동이 서로 끊기면 사용자는 다시 원래 하던 방식으로 돌아간다.

앱은 공원 목록과 주차장 정보를 보여줘야 했고, 위젯은 앱을 열지 않아도 지금 상태를 보여줘야 했다. 즐겨찾기는 기기에 남아야 했고, 알림 설정은 서버와 맞아야 했다. 길찾기는 네이버지도, 카카오맵, 티맵, 애플 지도, 구글 지도처럼 사용자가 쓰던 앱으로 넘겨야 했다.

SwiftUI로 만들었고, 안쪽에서는 상태, API 호출, 로컬 캐시, 위젯용 스냅샷이 서로 섞이지 않게 나눴다. SwiftData에는 공원과 주차장 상태, 갱신 시각을 저장했고, 앱과 WidgetKit extension이 App Group을 통해 같은 값을 읽게 했다.

위젯이나 길찾기에서 앱으로 들어온 사용자는 보던 공원과 화면으로 이어지게 했다.

앱을 열었을 때만 볼 수 있는 정보는 반쪽짜리다.

차량 판단과 위젯에서는 출발 전 확인이 특히 중요했다. 사용자가 굳이 앱을 열지 않아도 자주 가는 공원의 상황을 볼 수 있어야 했다. 그래서 위젯을 앱의 덤처럼 두지 않고, 앱 밖에서 바로 보는 화면으로 만들었다. 소형, 중형, 대형 위젯마다 남길 말과 버릴 말을 계속 고쳤다.

일반 위젯

일반 위젯에는 혼잡도, 날씨, 행사처럼 “오늘 이 공원에 가도 될까”를 가늠할 정보가 필요했다.

주차 위젯

차량 위젯에서는 추천 주차장과 남은 자리가 먼저 보여야 했다.

모바일 앱은 화면 안에서만 완성되지 않았다. 위젯, 알림, 딥링크, 캐시, 설정, 권한, 외부 지도 앱까지 이어져야 실제로 쓸 만했다. iOS 쪽에서 해야 할 일은 여러 입구가 같은 정보를 필요한 만큼만 빠르게 읽게 하는 것이었다.

초기에는 “화면을 어떻게 나눌까”를 먼저 생각했지만, 실제로는 “어느 순간에 어디서 답해야 할까”가 더 중요했다.

접점맡은 일조심한 점
비교, 상세 확인, 시설 탐색, 설정 변경첫 화면의 결정을 흐리지 않기
위젯출발 전 빠른 확인오래된 값을 최신처럼 보이게 하지 않기
알림사용자가 정한 조건 변화작은 변화로 계속 울리지 않기
딥링크보고 있던 공원과 화면으로 이어가기다시 첫 화면으로 돌려보내지 않기
외부 지도 앱실제 이동 시작공원 전체가 아니라 주차장/접근 지점에 가깝게 연결하기

이렇게 나누고 나서야 SwiftUI 화면, SwiftData 저장소, App Group, WidgetKit, 딥링크가 각각 왜 필요한지 설명할 수 있었다.

출발 직전의 순간부터 거꾸로 보니, 어디를 나눠야 하는지도 보였다.

위젯은 많이 넣는 곳이 아니었다

위젯을 만들면서 가장 많이 고민한 건 정보를 얼마나 넣을지가 아니었다. 무엇을 버릴지였다.

앱에서는 사용자가 스크롤하고, 탭하고, 상세 화면으로 들어갈 수 있다. 위젯에서는 그럴 시간이 없다. 한눈에 보고 멈추거나 출발한다. 그래서 차량 위젯은 “어느 주차장이 지금 더 나은가”, 일반 위젯은 “오늘 이 공원이 괜찮은가”에 집중했다.

이 차이는 데이터 저장 방식에도 영향을 줬다. 앱과 위젯이 같은 원본 데이터를 쓰더라도, 위젯에는 별도 스냅샷이 필요했다. 앱이 마지막으로 본 상태, 갱신 시각, 공원 선택, 위젯 종류가 안정적으로 같이 읽혀야 했다. 그래서 App Group으로 앱과 WidgetKit extension 사이에 값을 건네는 방식을 정했다.

딥링크도 중요했다. 위젯에서 공원을 열면 앱의 일반적인 첫 화면으로 들어가는 것이 아니라, 해당 공원과 그 화면으로 자연스럽게 이어져야 했다. 길찾기 버튼도 사용자가 선호하는 지도 앱으로 넘어갈 수 있어야 했다.

앱, 위젯, 알림, 외부 지도 앱, 로컬 캐시가 끊기지 않아야 그제야 쓸 만한 앱이 된다.

이때부터 iOS 설계는 화면 배치가 아니었다. 어느 접점에서 무엇을 바로 말하고, 같은 값을 어떻게 함께 읽게 할지가 문제였다.

위치 권한은 필요한 순간에만 물었다

위치 권한도 처음부터 묻지 않으려 했다. 한강자리는 위치를 몰라도 쓸 수 있어야 한다. 사용자가 직접 공원을 고르고 즐겨찾기에 넣을 수 있다면, 가까운 공원 정렬은 편의 기능이지 입장 조건이 아니다.

그래서 위치는 “앱을 쓰기 위해 내야 하는 입장료”가 아니라, 가까운 공원과 주차장을 더 쉽게 찾기 위한 선택지로 보는 편이 맞았다. 권한을 거절해도 앱이 무너지면 안 되고, 권한을 허용해도 서버로 위치를 보내지 않는 쪽을 유지했다.

권한을 늦게 묻기로 하니 안내 문구도 달라졌다. 권한 요청은 앱이 물을 수 있는 순간이 아니라, 사용자가 필요를 이해할 수 있는 순간에 나와야 했다. 가까운 공원을 보고 싶어 할 때, 현재 위치로 정렬하면 어떤 점이 편한지 설명할 수 있을 때 묻는 편이 자연스러웠다.

값이 없을 때도 이유를 보여줘야 했다

iOS에서 실제로 오래 붙잡은 것은 성공 화면보다 실패 화면일 때가 많았다. 데이터가 아직 없을 때, 네트워크가 실패했을 때, 마지막 값이 오래됐을 때, 특정 공원에는 해당 정보가 없을 때의 화면이 모두 필요했다.

처음에는 성공 화면부터 끝내고 이런 경우는 나중에 다루고 싶었다. 하지만 공공데이터 앱에서는 빈 상태가 자주 생긴다. 출처마다 갱신 속도가 다르고, 일부 공원에만 있는 정보도 있기 때문이다. 빈 화면에 이유가 없으면 사용자는 앱이 고장났다고 느낀다.

그래서 앱과 위젯 모두 “값이 없다”를 한 가지 상태로 묶지 않았다. 아직 불러오는 중인지, 가져오지 못했는지, 그 공원에는 없는 정보인지, 마지막 성공 값이 오래됐는지에 따라 안내가 달라져야 했다. 사용자는 그 차이로 기다릴지, 다른 공원을 볼지, 그냥 출발할지 정한다.

앱, 위젯, 알림의 역할을 겹치지 않게 했다

앱, 위젯, 알림은 같은 데이터를 보지만 같은 일을 하지 않는다.

  • 앱: 비교하고, 상세를 확인하고, 근처 시설을 찾고, 설정을 바꾸는 곳.
  • 위젯: 출발 전에 빠르게 확인하는 곳.
  • 알림: 사용자가 정한 조건의 변화만 알려주는 곳.
  • 외부 지도 앱: 실제 이동으로 넘어가는 곳.

이렇게 나누지 않으면 한 화면과 한 알림에 너무 많은 일을 맡기게 된다. 앱 첫 화면에는 모든 정보를 넣고 싶어지고, 위젯은 읽기 어려울 만큼 값을 보여주고, 알림은 작은 변화까지 계속 보내게 된다. 그러면 정보가 늘어도 사용자는 더 피로해진다.

위젯은 특히 조심해야 했다. 작은 위젯에 정보를 많이 넣으면 똑똑해 보이지만, 실제로는 읽기 어렵다. 반대로 너무 줄이면 “그래서 가도 된다는 거야?”라는 질문에 답하지 못한다. 그래서 일반 위젯은 “오늘 이 공원이 괜찮은가”에, 차량 위젯은 “지금 차를 가져가도 되는가”에 답하게 했다.

기기에 남긴 값으로 앱과 위젯을 맞췄다

SwiftData와 App Group은 네트워크 응답을 잠깐 저장하는 장치로만 쓰이지 않았다. 앱과 위젯이 같은 값을 같은 의미로 읽게 하는 기준이었다.

앱은 사용자가 마지막으로 고른 공원, 즐겨찾기, 보고 있던 화면, 마지막 갱신 시각을 알고 있어야 했다. 위젯은 앱이 꺼진 상태에서도 그 정보를 안전하게 읽어야 했다. 네트워크가 잠시 실패해도 완전히 빈 화면을 보여주기보다는 마지막으로 확인된 값과 그 시각을 보여주는 편이 더 나았다.

오래된 스냅샷을 최신처럼 보이게 하면 위젯은 오히려 위험한 화면이 된다. 그래서 위젯에는 값 자체만큼 갱신 시각과 상태 안내가 중요했다.

위젯에서 보던 공원을 앱에서 바로 열었다

위젯에서 앱을 열었는데 다시 공원 목록 첫 화면부터 시작하면 맥락이 끊긴다. 사용자는 이미 위젯에서 특정 공원을 보고 들어왔다. 그러면 앱은 그 공원과 그 화면을 이어받아야 한다.

외부 지도 앱도 마찬가지다. 주차장을 보고 “길찾기”를 눌렀을 때 사용자가 평소 쓰는 지도 앱으로 바로 넘어가야 한다. 여기서 한 번 더 고르게 하거나, 공원 전체 주소로만 보내면 주차장 입구에서 다시 헤매게 된다.

한강자리는 이동 전후의 짧은 순간에 쓰이는 앱이라서, 작은 끊김이 곧 피로가 된다.

결국 iOS 앱에서 중요한 것은 화면 개수가 아니었다. 사용자가 이미 보고 있던 공원과 선택을 앱 안팎에서 계속 이어주는 일이었다. 그래서 위젯, 알림, 딥링크를 별도 기능 목록으로 두지 않고 같은 확인 과정 안에 묶었다.

이미지 확대