2025. 2. 19. 23:20ㆍRetrospect
프로젝트의 시작 (Project. Strict Money Checking)
호땡이와 함께 분당선 도곡역 테라로사 카페에서 만난 2024년 9월. 이 글을 작성하는 2025년 2월이 된 지금, 그날 어떤 이야기를 나누었는지는 가물가물하다. 🫨 하지만 확실한 건, 우리는 소비 습관에 대해 이야기하다가 자연스럽게 ‘가계부 앱을 만들어보자’는 얘기로 흘러갔다.
우리는 Swift 6의 Strict Concurrency에 대해 이야기하고 있었다. Strict Concurrency는 컴파일 타임에 데이터 레이스를 방지해 코드의 안정성을 보장하는 새로운 패러다임이다. 그렇다면, Strict Concurrency가 코드의 안정성을 보장하듯 우리의 소비 습관도 체계적으로 관리할 수 있지 않을까? ㅋㅋㅎ 그렇게 우리의 프로젝트가 시작되었다.
![[스크린샷 2025-02-14 오후 9.29.14.png | 300]]
자동 등록, 정말 편할까? 😥
💀 “요즘 누가 직접 가계부를 써? 토스나 뱅크샐러드에서 카드 내역이 자동 등록되는데!” 💀
대부분의 핀테크 가계부 앱에서 자동 등록 기능을 제공한다. 하지만 우리는 오히려 ‘자동 등록이 불편하다’는 점에서 출발했다.
가계부를 쓰는 이유는 소비를 줄이기 위해서다. 그런데 카드 내역이 자동으로 입력되면 소비를 직접 인지하는 과정이 사라진다. 결국 “이번 달에 얼마나 썼지?” 하고 돌아보는 순간이 사라지고, 돈이 어디로 빠져나갔는지 흐릿해진다. 자동 등록이 오히려 소비 습관을 통제하는 데 방해가 되는 셈이다.
그렇다고 기존 가계부 앱이 편리한 것도 아니었다. 여러 소비 내역을 한 번에 등록할 수 없고, 같은 금액을 같은 장소에서 소비해도 매번 일일이 입력해야 했다. 게다가 기능이 너무 많아 오히려 복잡했다. “필요한 기능만 담아, 사용자가 직접 소비를 인지할 수 있는 가계부를 만들어보자.” 이런 고민 끝에 Strict Money Checking 프로젝트가 시작되었다.
개발 과정에서의 고민
차별화된 핵심 기능을 제공하려면 기본 기능이 탄탄해야 했다. 그러나 SwiftUI로 한정된 시간 내에 가계부의 필수 기능을 구현하는 것은 쉽지 않았다. (계산을 틀리는 가계부 앱은 만들 수 없으니까 .. 🤣)
기본 달력
가계부의 핵심 기능 중 하나인 달력은 주/월 전환과 좌우 무한 스와이프를 지원해야 했다. 주/월 전환은 비교적 단순했다. SwiftUI는 상태 기반 렌더링을 제공하므로, 표시할 데이터를 주 또는 월 단위로 변경하는 것만으로 자연스럽게 전환이 이루어졌다. 하지만 좌우 무한 스와이프 구현에서는 많은 시행착오를 겪었다.
초기 설계는 3개의 뷰를 배치하고 contentOffset을 조정하는 방식이었다. 예를 들어, 3월에서 2월로 스와이프하면 뷰의 위치를 초기화하고 데이터를 갱신해 자연스럽게 이어지는 것처럼 보이게 했다. 하지만 SwiftUI의 onScrollPhaseChange만으로는 스크롤 타이밍을 완벽하게 제어할 수 없어 빠른 스와이프 시 문제가 발생했다. 결국 UIPageViewController 기반 설계로 방향을 전환했다. UIViewControllerRepresentable을 활용해 SwiftUI에서 사용할 수 있도록 감싸고, 좌우 페이지를 동적으로 생성해 무한 스크롤을 구현했다. 최종적으로 안정적인 동작을 확보했지만, 첫 번째 접근법을 최적화하는 데 너무 많은 시간을 쏟은 점은 아쉬움으로 남았다.
[!warning] contentOffset 초기화 타이밍을 맞추기 위해 onScrollPhaseChange 뷰모디파이어에서 oldPhase == .decelerating && newPhase == .idle 시점을 활용했으나, 빠르게 스와이프할 경우 정상적으로 감지되지 않는 문제가 있었다.
로컬리제이션
한국뿐만 아니라 해외 가계부 시장도 궁금했다. 그래서 처음부터 영어와 일본어 로컬라이제이션을 함께 진행했다.
문자
Xcode 15부터는 String Catalog를 통해 로컬라이제이션을 쉽게 적용할 수 있다. 우리는 한국어를 key 값으로 사용하고, 번역은 ChatGPT를 활용했다. 문자열 로컬라이제이션 자체는 큰 고민 없이 진행했지만, 결국 수작업이 많았다. (노가다였음 💦)
날짜
날짜 형식은 언어에 따라 다르게 표시된다. 예: 2024년 12월 24일 (금) → December 24, 2024 (Fri)
처음에는 DateFormatter의 locale을 현재 국가에 맞추고, setLocalizedDateFormatFromTemplate을 활용하면 해결될 것이라 생각했다. 하지만 국가별로 세부적인 분기가 필요해 복잡해졌다. 이를 해결하기 위해 String Catalog에 “현재 날짜” key를 추가하고, 각 언어별 형식을 미리 설정한 후, dateFormat으로 적용했다.
년/월/일 설정
// String Catalog 설정
// 한국: yyyy년 M월 d일 (E)
// 영어: MMM d, yyyy (E)
// 일본어: yyyy年 M月 d日 (E)
let formatter = DateFormatter()
formatter.locale = Locale(identifier: Locale.preferredLanguages.first!)
formatter.dateFormat = String(smcKey: "현재날짜", table: .recordSpending)
return formatter
요일 설정
let formatter = DateFormatter()
formatter.locale = Locale(identifier: Locale.preferredLanguages.first!)
formatter.setLocalizedDateFormatFromTemplate("E")
return formatter
금액 단위
금액 로컬라이제이션은 정책부터 확립해야 했다. 우리는 앱스토어 국가 기준으로 금액 단위를 결정하기로 했다.
- 한국 앱스토어에서 다운로드 → 기본 통화는 원(₩)
- 일본 앱스토어에서 다운로드 → 기본 통화는 엔(¥)
- 미국 앱스토어에서 다운로드 → 기본 통화는 달러($)
- 이후 언어 변경 시에도 금액 단위는 유지
이렇게 정한 이유는 환율 반영 문제 때문이다. 앱 내에서 실시간 환율을 적용하는 것은 가계부의 핵심 기능과 거리가 멀고, 추가적인 유지보수 부담도 발생하기 때문이다. 따라서 금액 단위는 사용자의 앱스토어 국가 기준으로 고정하고, 이를 로컬에 저장하도록 구현했다.
추가적으로, 달러($)는 소수점 두 자리까지 표기해야 한다는 점을 늦게 깨달았다. 이를 반영하기 위해 금액 단위를 Int에서 Double로 변경하고, 영어권 사용자를 위해 금액 입력 키보드에서 소수점 입력을 지원하도록 UI도 수정했다. (근데, 영어를 지원하는 다른 가계부 앱들은 왜 대부분 소수점 입력을 제공하지 않을까.. 🤔)
돌아보며
앱을 배포한 지 일주일이 지났고, 현재까지 약 30명이 다운로드했으며 일일 평균 9명이 사용 중이다. 별다른 마케팅 없이 이 정도의 유입이 있었다는 점에서, 실제 사용자들에게 가치 있는 앱을 만들었다는 점이 의미가 있다고 생각한다. 아직은 기본적인 기능만 제공하고 있지만, 앞으로 지속적인 개선을 통해 완성도를 높여갈 계획이다. 특히, AB 테스트를 돌려보며 다양한 시도를 해보고 싶다. 직접 원하는 방향으로 기능을 실험하고 최적화하는 과정도 하나의 재미있는 도전이 될 것 같다.
이번 프로젝트는 1.0 버전 배포까지 비교적 순조롭게 진행되었지만, 개인적으로 바쁜 시기가 있어 약 1~2주간 개발을 진행하지 못한 적이 있었다. 다행히도, 함께하는 동료들의 열정적인 모습에 다시 동기부여를 얻어 집중할 수 있었다. 이번 경험을 통해, 주변의 좋은 동료들이 얼마나 중요한지 다시 한번 깨달았다. 앞으로도 함께 성장하며 더 나은 서비스를 만들어가고 싶다! 잘 부탁해요 😎
그리고 SwiftUI는 코드 단 몇 줄로 멋진 애니메이션과 UI를 구현할 수 있는 강력한 도구다. 잘 활용해서 아름답고 직관적인 앱을 만들어보자!!