앱을 열기 전에도 이어져야 하는 iOS 경험
위젯, 위치 권한과 빈 상태를 앱을 열기 전후의 한 흐름으로 설계한 과정
사용자는 위젯과 알림, 딥링크에서도 iOS 앱의 정보를 만났다.
처음에는 앱 화면 하나와 위젯 하나면 충분할 줄 알았다. 직접 써보려 하니 iOS 쪽에서 해야 할 일이 빠르게 늘었다.
특히 출발 직전의 사용자는 차분히 앱을 탐색하지 않을 것이라고 가정했다. 나부터도 집에서 나가기 직전이거나 엘리베이터 앞이거나 이미 차에 앉아 있을 때가 많았다. 그 짧은 순간에 앱, 위젯, 알림, 지도 앱 이동이 서로 끊기면 원래 하던 방식으로 돌아갈 것이라고 봤다.
앱에는 공원 목록과 주차장 정보를 담았고, 위젯에는 앱을 열지 않아도 보이는 지금 상태를 담았다. 즐겨찾기는 기기에 저장했고, 알림 설정은 서버와 동기화했다. 길찾기는 네이버지도, 카카오맵, 티맵, 애플 지도, 구글 지도처럼 사용자가 쓰던 앱으로 넘겼다.
SwiftUI로 만들었고, 안쪽에서는 상태, API 호출, 로컬 캐시, 위젯용 스냅샷이 서로 섞이지 않게 나눴다. SwiftData에는 공원과 주차장 상태, 갱신 시각을 저장했고, 앱과 WidgetKit extension이 App Group을 통해 같은 값을 읽게 했다.
위젯이나 길찾기에서 앱으로 들어온 사용자는 보던 공원과 화면으로 이어지게 했다.
차량 판단과 위젯에서는 출발 전 확인을 우선했다. 사용자가 앱을 열지 않아도 자주 가는 공원의 상황을 볼 수 있게 위젯을 앱 밖에서 바로 보는 화면으로 만들었다. 소형, 중형, 대형 위젯마다 남길 말과 버릴 말을 계속 고쳤다.

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

차량 위젯에는 추천 주차장과 남은 자리를 맨 위에 놓았다.
위젯, 알림, 딥링크, 캐시, 설정, 권한과 외부 지도 앱은 같은 공원 상태를 이어받아야 했다. iOS 쪽에서는 각 접점이 필요한 정보만 빠르게 읽게 했다.
iOS 쪽에서는 사용자가 확인하는 순간마다 어느 접점에서 답할지를 기준으로 화면을 나눴다.
| 접점 | 맡은 일 | 조심한 점 |
|---|---|---|
| 앱 | 비교, 상세 확인, 시설 탐색, 설정 변경 | 첫 화면의 결정을 흐리지 않기 |
| 위젯 | 출발 전 빠른 확인 | 오래된 값을 최신처럼 보이게 하지 않기 |
| 알림 | 사용자가 정한 조건 변화 | 작은 변화로 계속 울리지 않기 |
| 딥링크 | 보고 있던 공원과 화면으로 이어가기 | 다시 첫 화면으로 돌려보내지 않기 |
| 외부 지도 앱 | 실제 이동 시작 | 공원 전체가 아니라 주차장/접근 지점에 가깝게 연결하기 |
이 구분에 맞춰 SwiftUI 화면은 표시를, SwiftData 저장소는 상태 보관을, App Group은 앱과 위젯 간 값 전달을, WidgetKit과 딥링크는 위젯 갱신과 화면 연결을 맡도록 나눴다.
위젯에서 덜어낸 정보
차량 위젯은 “어느 주차장이 지금 더 나은가”, 일반 위젯은 “오늘 이 공원이 괜찮은가”에 필요한 값만 남기고 나머지는 뺐다.
앱에서는 사용자가 스크롤하고 탭해서 상세 화면으로 들어갈 수 있다. 위젯에서는 그럴 시간이 없다. 한눈에 보고 멈추거나 출발한다.
이 차이는 데이터 저장 방식에도 영향을 줬다. 앱과 위젯이 같은 원본 데이터를 쓰더라도 위젯에는 별도 스냅샷을 뒀다. 앱이 마지막으로 본 상태, 갱신 시각, 공원 선택, 위젯 종류를 안정적으로 같이 읽도록 App Group으로 앱과 WidgetKit extension 사이에 값을 건네는 방식을 정했다.
딥링크에서도 위젯에서 공원을 열면 앱의 일반적인 첫 화면 대신 해당 공원과 그 화면으로 바로 이어지게 했다. 길찾기 버튼도 사용자가 선호하는 지도 앱으로 바로 넘어가게 했다.
필요한 순간의 위치 권한
위치 권한도 처음부터 묻지 않으려 했다. 한강자리는 위치를 몰라도 쓸 수 있어야 한다. 사용자가 직접 공원을 고르고 즐겨찾기에 넣을 수 있다면, 가까운 공원 정렬은 편의 기능이지 입장 조건이 아니다.
위치는 가까운 공원과 주차장을 찾을 때만 쓰는 선택 기능으로 뒀다. 권한을 거절해도 공원을 직접 고를 수 있고, 권한을 허용해도 좌표는 서버로 보내지 않았다.
권한 요청은 사용자가 가까운 공원을 찾으려 할 때 보여줬다. 현재 위치로 정렬하면 무엇이 달라지는지 그 자리에서 설명했다.
값이 없을 때 보여줄 이유
iOS에서는 실패 화면을 오래 붙잡고 고쳤다. 데이터가 아직 없을 때, 네트워크가 실패했을 때, 마지막 값이 오래됐을 때, 특정 공원에는 해당 정보가 없을 때의 화면이 모두 필요했다.
처음에는 성공 화면부터 끝내고 이런 경우는 나중에 다루고 싶었다. 하지만 공공데이터 앱에서는 빈 상태가 자주 생긴다. 출처마다 갱신 속도가 다르고, 일부 공원에만 있는 정보도 있기 때문이다. 빈 화면에 이유가 없으면 사용자는 앱이 고장났다고 느낀다.
그래서 앱과 위젯 모두 “값이 없다”를 한 가지 상태로 묶지 않았다. 아직 불러오는 중인지, 가져오지 못했는지, 그 공원에는 없는 정보인지, 마지막 성공 값이 오래됐는지에 따라 안내를 다르게 했다. 사용자는 그 차이로 기다릴지, 다른 공원을 볼지, 그냥 출발할지 정한다.
앱·위젯·알림의 역할 분담
작은 위젯에 정보를 많이 넣으면 읽기 어렵고, 너무 줄이면 출발 판단에 필요한 값이 빠진다. 일반 위젯은 “오늘 이 공원이 괜찮은가”에, 차량 위젯은 “지금 차를 가져가도 되는가”에 필요한 값만 남겼다.
역할을 나누지 않으면 한 화면과 한 알림에 너무 많은 일을 맡기게 된다. 앱 첫 화면에는 모든 정보를 넣고 싶어지고, 위젯은 읽기 어려울 만큼 값을 보여주고, 알림은 작은 변화까지 계속 보내게 된다. 그러면 정보가 늘어도 사용자는 더 피로해질 것이라고 판단했다.
앱과 위젯을 맞춘 기기 저장값
SwiftData와 App Group에는 네트워크 응답, 앱과 위젯이 함께 읽을 공원 선택, 즐겨찾기와 갱신 시각을 저장했다.
앱은 사용자가 마지막으로 고른 공원, 즐겨찾기, 보고 있던 화면, 마지막 갱신 시각을 기억했다. 위젯은 앱이 꺼진 상태에서도 그 정보를 안전하게 읽었다. 네트워크가 잠시 실패해도 완전히 빈 화면을 보여주기보다는 마지막으로 확인된 값과 그 시각을 보여주는 편이 더 나았다.
위젯에서 앱으로 이어지는 공원
위젯에서 앱을 열었는데 다시 공원 목록 첫 화면부터 시작하면 맥락이 끊긴다. 사용자는 이미 위젯에서 특정 공원을 보고 들어왔다. 그러면 앱은 그 공원과 그 화면을 이어받아야 한다.
외부 지도 앱도 마찬가지다. 주차장을 보고 “길찾기”를 눌렀을 때 사용자가 평소 쓰는 지도 앱으로 바로 넘어가야 한다. 여기서 한 번 더 고르게 하거나, 공원 전체 주소로만 보내면 주차장 입구에서 다시 헤매게 된다.
그래서 딥링크는 위젯이 보여준 공원과 화면을 그대로 열도록 했다.
iOS 앱에서는 사용자가 보던 공원과 선택을 앱 안팎에서 이어 주는 데 집중했다. 위젯, 알림과 딥링크는 각각 그 공원 상태를 넘겨받아 같은 확인 과정을 이어 갔다.
Comments
아직 댓글이 없습니다. 첫 댓글을 남겨주세요.
검토 대기 중