이번 프로젝트는 아이디어를 만들지 않았다
Flutter 실습 기간이 주어졌을 때, 첫 번째 고민은 “무엇을 만들지”가 아니었다. “내가 실제로 이해하는 문제가 뭔지”였다.
억지로 아이디어를 만드는 것과, 이미 알고 있는 불편에서 출발하는 것은 시작부터 다르다. 기능 우선순위를 잡을 때도, 예외 케이스를 설계할 때도, 내가 이해하는 문제에서 출발한 프로젝트가 훨씬 빠르다.
그래서 골랐다. 교대근무자를 위한 급여 계산기.
호텔, 병원, 물류센터처럼 교대근무가 일상인 환경에서는 급여 계산이 복잡하다. 시급에 시간을 곱하면 끝이 아니다. 야간 시간대에 걸쳤는지, 공휴일이었는지, 자정을 넘겼는지, 휴게 시간은 얼마였는지에 따라 수령액이 달라진다. HR과 재무 업무를 경험한 사람으로서, 이 복잡함이 얼마나 실질적인 문제인지 알고 있었다. 내가 잘 아는 맥락이 그대로 제품의 출발점이 됐다.
달력을 버리고 나서야 앱의 목적이 선명해졌다
초안에는 달력이 있었다. 날짜를 선택해서 근무를 입력하는 구조. 시각적으로 직관적이고, 완성하면 보기도 좋다.
그런데 Flutter 입문 단계에서 달력 커스터마이징은 생각보다 깊은 작업이다. 선택 가능한 날짜 범위, 마킹 스타일, 월 이동 인터랙션 — 이것들을 제대로 구현하려면 학습 목표가 달력 자체가 되어버린다.
더 중요한 질문은 따로 있었다. 이 앱에서 달력이 진짜 필요한가?
이 앱의 핵심은 근무 기록을 예쁘게 시각화하는 것이 아니다. 야간수당과 휴일수당이 정확하게 반영된 급여 합계를 빠르게 확인하는 것이다. 달력을 없애고 리스트로 바꾸니, 오히려 앱이 무엇을 위한 것인지 더 선명해졌다.
최종 화면 구조는 3개로 정리됐다.
- 홈 — 이번 달 예상 급여 합계 + 근무 기록 카드 리스트
- 근무 입력 — 날짜 / 시작·종료 시간 / 휴게 시간 / 공휴일 여부
- 설정 — 시급 및 수당 규칙
기능을 줄인 게 아니라, 필요한 것만 남긴 것이다. 그리고 범위를 줄인 뒤 잡은 이 구조가, 이후 모든 기능 추가 결정의 기준이 됐다.
기술 선택: 화면과 로직을 같은 언어로
Flutter를 택한 이유는 명확했다. 화면과 계산 로직을 Dart 하나로 처리할 수 있다는 것.
처음에는 어떤 언어로 시작해야 할지 혼동이 있었다. 하지만 모바일 앱에서 별도 백엔드 언어를 같이 배우는 것은 7일이라는 기간 안에 맞지 않았다. Flutter는 UI와 로직을 단일 언어로 다루고, Dart는 Java와 문법적으로 닮은 부분이 많아 진입 장벽이 낮았다.
폴더 구조는 처음부터 역할을 나눠 잡았다.
lib/
├── models/ — ShiftEntry 등 데이터 구조
├── providers/ — ChangeNotifier 기반 상태 관리
├── screens/ — 홈, 근무 입력, 설정 화면
├── utils/ — 급여 계산 함수
└── widgets/ — 재사용 UI 컴포넌트
단일 파일에 다 넣지 않은 것이 나중에 가장 도움이 됐다. 기능이 확장될수록 이 분리가 효과를 발휘했다.
상태 관리는 Provider/ChangeNotifier로, 로컬 저장은 SharedPreferences로 정리했다. 서버 없이도 근무 기록이 앱을 껐다 켜도 이어지는 경험을 줄 수 있었다. 초기 MVP에서 그것으로 충분했다.
프롬프트는 코드 요청이 아니라 기획서였다
UI는 Stitch로, 코드 구조와 로직은 Jules로 시작했다.
초반에는 막연하게 요청했다. “Flutter로 급여 계산기 만들어줘.” 결과는 입력 필드 몇 개와 버튼 하나. 계산 로직은 없었다.
바뀐 건 프롬프트의 밀도였다. 어떤 입력값을 받는지, 계산 규칙이 무엇인지, 상태는 어떻게 갱신되는지, 저장은 어디에 하는지까지 명시하자 산출물이 달라졌다. 디자인 요청과 구현 요청을 분리한 것도 효과가 있었다. 화면을 먼저 Stitch로 잡고, 그 구조에 맞는 로직을 Jules에 요청하니 두 결과물이 충돌하지 않았다.
이 프로젝트에서 배운 것 중 하나는, 생성형 도구는 내가 정확히 알고 있을수록 더 잘 작동한다는 점이다. 도구를 잘 쓰는 능력은 결국 문제를 구조화하는 능력과 같았다. 코드를 만들기 전에, 내가 먼저 무엇을 원하는지 명확히 정리하는 과정이 필요했다.
이 앱에서 진짜 어려웠던 건 Flutter가 아니었다
화면보다 어려웠던 건 급여 계산 규칙을 코드로 번역하는 일이었다.
교대근무 급여를 처음 접하는 사람에게는 이렇게 설명하면 된다. 밤 11시에 출근해서 다음 날 오전 7시에 퇴근하는 8시간 근무를 생각해보자. 단순 계산으로는 시급 × 8이다. 하지만 실제로는 다르다. 밤 10시부터 다음 날 오전 6시까지의 야간 구간이 이 근무와 겹친다. 그 구간은 수당 배율이 다르게 적용된다. 그날이 공휴일이면 또 달라진다. 중간에 휴게 시간이 있었다면 실근로는 그만큼 줄어든다.
개발 관점에서 가장 까다로운 케이스는 자정을 넘기는 근무였다.
// 자정 넘김 처리
DateTime end = DateTime(date.year, date.month, date.day, endHour, endMin);
if (end.isBefore(start)) {
end = end.add(const Duration(days: 1));
}
야간 구간 겹침 계산, 휴게 시간 차감 후 실근로 환산, 공휴일 분기, 연장근로 여부 판단까지 더해지면, 단순해 보이는 계산 함수 하나가 상당한 경계값 처리를 담게 된다. HR 경험이 없었다면 이 항목들을 처음부터 빠짐없이 정의하기 어려웠을 것이다.
계산기에서 제품으로
핵심 계산 로직이 잡히고 나서, 개발의 성격이 바뀌기 시작했다.
처음에는 “계산이 맞게 작동하는가”가 기준이었다. 근무를 입력하면 Provider를 통해 홈 화면의 합계가 즉시 갱신되는 피드백 루프를 만드는 것, 그리고 기록이 앱을 껐다 켜도 남아 있는 것. 이 두 가지가 초기 MVP를 완성으로 만든 요소였다.
그 다음부터는 기준이 달라졌다. 통계 페이지, PDF/CSV export, RevenueCat 기반 프리미엄 구독, Google Drive 백업, 근무 패턴 자동 생성기, 알림 설정까지 붙었다. 기능 하나가 더해질 때마다 앱의 성격이 조금씩 바뀌었다.
특히 export와 백업 기능이 붙는 순간, 느낌이 달라졌다. 사용자가 자신의 데이터를 PDF로 뽑고 다른 기기로 백업할 수 있다는 건 단순한 편의 기능이 아니다. 그때부터 “토이 프로젝트”라는 감각이 사라지기 시작했다.
처음에는 계산기였다. 기록이 붙으면서 기록 관리 앱이 됐다. 통계와 export가 붙으면서 리포트 도구가 됐다. 백업과 구독이 붙으면서 서비스 형태를 갖추기 시작했다.
마지막 10%
기능이 어느 정도 갖춰지고 나서, 진짜 시간을 잡아먹은 건 다른 것들이었다.
앱 아이콘 교체, 로딩 애니메이션 수정, 삭제 확인 다이얼로그, 법률 온보딩 페이지 문구 수정, AndroidManifest와 build.gradle 설정, 알림 권한, 배포 서명 설정.
하나하나는 작아 보인다. 하지만 전부 더하면 예상보다 훨씬 오래 걸린다. 아이콘 하나를 바꾸려면 해상도별 이미지가 필요하고, 배포 설정을 건드리면 빌드 에러가 따라온다. 삭제 확인 다이얼로그는 전체 UX 흐름을 다시 검토하게 만든다.
앱은 기능으로 완성되지 않는다. 이 마지막 10%가 전체 완성도를 결정하는 경우가 많다.
지금은 구글 플레이 등록 절차 중이다
현재는 앱을 실제로 구글 플레이에 올리는 과정을 진행하고 있다.
개발자 계정 등록 비용 25달러를 납부했다. 개인정보 처리 방침, 앱 콘텐츠 등급, 타겟 대상, 수익화 정책 설정까지 마쳤다. 지금은 스토어 등록에 필요한 스크린샷과 그래픽 이미지를 캡처하고 있는 단계다.
앱을 만드는 것과 앱을 출시하는 것은 다른 작업이다. 구글 플레이 콘솔에 들어가보면, 스토어 등록 정보만 해도 짧은 설명, 긴 설명, 전화기 스크린샷, 7인치 태블릿 스크린샷, 피처드 이미지, 앱 아이콘까지 준비해야 한다. 앱 자체가 아니라 앱을 소개하는 작업도 이만큼의 공이 든다.
코드를 짜는 것보다 덜 흥미롭게 느껴질 수 있는 작업이지만, 이 과정을 직접 겪어봐야 배포 경험이 생긴다. 지금 그 과정 안에 있다.
느낀 점
이번 프로젝트에서 가장 크게 체감한 건, 내가 잘 아는 문제를 고른 것이 얼마나 실질적인 차이를 만드는가 하는 점이었다.
기능 우선순위를 잡을 때도, 예외 케이스를 정의할 때도 “이건 현장에서 실제로 중요한 부분이다”라고 바로 판단할 수 있었다. 모르는 분야였다면 처음부터 다시 조사해야 했을 것들이, 이미 머릿속에 있었다. 문제를 아는 것과 모르는 것의 차이는 기능 수가 아니라 결정 속도에서 드러났다.
그리고 범위를 줄이는 결정의 효과다. 달력을 버리고 리스트를 택했을 때, 앱의 목적이 더 선명해졌다. 선명해진 목적이 이후 모든 결정의 기준이 됐다. 기능을 줄이는 것이 오히려 더 좋은 앱을 만든다는 걸 이 프로젝트에서 직접 확인했다.
배운 점
기술적으로는 Flutter/Dart의 흐름, Provider로 상태를 관리하는 방식, SharedPreferences로 로컬 저장을 구성하는 법을 익혔다.
하지만 더 오래 남을 것은 구조를 먼저 생각하는 습관이다. 모델, 상태, 화면을 처음부터 분리했기 때문에 기능을 하나씩 붙여가는 과정이 비교적 깨끗했다. 단일 파일에 다 넣었다면 통계 페이지나 export 기능을 붙일 때 이미 뒤엉켜 있었을 것이다.
생성형 도구에 대해서도 배웠다. 막연한 요청과 구조화된 요청의 결과 차이를 직접 경험했다. 도구를 잘 쓰는 능력은 내가 무엇을 원하는지 먼저 정확히 아는 것과 같다. 프롬프트를 잘 쓰는 것은 결국 기획을 잘 하는 것이었다.
다음에 다듬고 싶은 것
계산 로직의 단위 테스트는 초기에 한 번 작성했지만, 기능이 확장되면서 커버리지가 따라가지 못했다. 야간 구간 겹침, 자정 넘김, 공휴일 분기 같은 경계값 케이스는 테스트로 반드시 보호해야 한다. 계산이 틀리면 신뢰를 잃는 앱이기 때문이다.
법률 온보딩 페이지에 수당 계산 기준을 안내하긴 했지만, 이 앱의 계산이 참고용이라는 점을 더 명확하게 전달하고 싶다. 실제 급여는 회사 규정과 계약 조건에 따라 다를 수 있고, 사용자가 그것을 쉽게 확인할 수 있어야 한다.
그리고 공공데이터 API를 연동해 공휴일 여부를 자동으로 처리하고 싶다. 지금은 사용자가 직접 토글로 입력하는 방식이지만, 날짜를 입력하면 공휴일이 자동으로 반영되면 입력 흐름이 훨씬 자연스러워질 것이다.
마치며
WorkWage는 Flutter 문법을 익히는 연습이기도 했지만, 그보다 더 크게는 내가 실제로 이해하는 문제를 제품으로 바꾸는 훈련이었다.
지금 구글 플레이 등록 절차를 진행하면서, 스토어에 올라갈 스크린샷을 하나씩 찍고 있다. 코드를 처음 짠 날부터 이 화면까지 오는 데 생각보다 많은 단계가 있었다. WorkWage는 Flutter를 익히기 위한 연습을 넘어, 실제 문제를 작은 제품으로 끝까지 밀어붙여 보는 경험이 됐다.
Community
Comments
Comments appear immediately. Use report if something needs review.
No comments yet.