[나의 앱 개발기] 코딩 모르는 직장인이 아버지를 위한 비밀번호 보관함 앱을 직접 만든 이야기
아버지 전화가 왔어요. "야, 쿠팡 비밀번호가 뭐였지?" 일주일 전에도 같은 전화였거든요. 네이버, 카카오, 병원 앱, 은행 앱… 요즘 앱 하나에 비밀번호 하나씩이니까 70대 어르신한테는 진짜 전쟁이에요. 메모장에 적어두셨다가 잃어버리고, 핸드폰 메모앱에 저장했다가 어디 있는지 모르고.
그때 결심했습니다. "내가 직접 만들자. 아버지 폰에 딱 맞는 비밀번호 보관함을." 저는 개발자가 아니에요. Google Apps Script로 직원 관리 툴 정도 만들어본 게 전부인 직장인이고요. 그런데 Claude AI와 함께라면 해볼 만하겠다 싶었어요. 결론부터 말씀드리면, 완성했습니다. 그 과정이 생각보다 훨씬 험난했고요.
* 처음엔 단순하게 시작했다.
첫 번째 버전은 그냥 HTML 파일 하나였어요. localStorage에 데이터 저장하고, PIN 4자리로 잠금하고, 카드 형태로 사이트명·아이디·비밀번호 표시하는 구조. Claude가 한 번에 만들어줬는데, 처음 열어봤을 때 "오 이거 괜찮은데?" 싶었어요.
근데 문제가 바로 나왔습니다. 브라우저 캐시를 지우면 데이터가 통째로 날아간다는 거예요. localStorage는 브라우저 저장소라 캐시 삭제하면 끝이거든요. 아버지 폰에서 잘 쓰다가 어느 날 데이터가 다 사라지면 저한테 전화가 쏟아질 게 뻔했어요. 방향을 바꿔야 했습니다.
* Google Apps Script로 백엔드를 만들어보다.
평소에 GAS를 좀 써봤으니까 자연스럽게 여기로 방향을 잡았어요. Google Sheets를 데이터베이스로 쓰고, GAS 웹앱으로 서비스하는 방식이에요. 스프레드시트 ID까지 세팅하고, 함수도 다 만들었는데 여기서 생각지도 못한 문제들이 터졌어요.
| 문제 | 원인 | 해결 여부 |
|---|---|---|
| GAS HTML에서 동적 함수 호출 불가 | google.script.run은 고정 함수명만 허용 | 구조 변경으로 해결 |
| em dash(—) 문자로 SyntaxError | 특수문자가 코드에 혼입 | 문자 교체로 해결 |
| 무한 로딩 | screenLoading이 active 상태로 시작 | 초기화 순서 수정 |
| id.slice is not a function 오류 | Sheets에서 숫자로 넘어온 ID를 String 처리 안 함 | String() 변환 추가 |
특히 PIN 입력해도 아무 반응이 없는 버그가 제일 오래 걸렸어요. phase 변수가 'loading' 상태인 채로 PIN을 입력하고 있었던 거더라고요. GAS에서 getPin()을 비동기로 호출하는데, 응답 오기 전에 이미 숫자를 누르고 있으니 checkPin 함수가 실행 자체가 안 됐던 거예요. alert()로 디버깅하면서 하나씩 잡았습니다.
* GAS의 한계, 결국 인정하다.
버그는 잡았는데 근본적인 문제가 남았어요. 아이폰 사파리에서 GAS 웹앱 URL을 열면 "현재 파일을 열 수 없습니다"가 뜨더라고요. 사파리가 GAS URL을 구글 드라이브 파일로 인식해서 막아버리는 거예요. 크롬에서는 잘 되는데, 아버지가 쓰는 안드로이드 갤럭시에서 기본 브라우저로 열면 안 된다는 것도 같은 문제였어요.
그리고 결정적으로 느렸어요. GAS는 콜드 스타트 문제가 있어서, 요청이 없으면 서버가 잠들어 있다가 첫 호출 시 깨어나는 데 3~5초가 걸리거든요. PIN 입력하고 메인 화면 가는 데 5초 기다리는 건 어르신한테 너무 불편한 경험이에요. 뭔가 새로운 방법이 필요했습니다.
* GitHub Pages + Supabase PWA로 완전히 갈아타다.
Claude가 제안한 방향이 이거였어요. GitHub Pages로 정적 HTML을 호스팅하고, Supabase를 데이터베이스로 쓰는 PWA. 처음엔 이름만 들어도 복잡해 보였는데, 역할을 나눠보면 간단합니다.
GitHub Pages → 앱 화면 호스팅 (무료)
Supabase에서 테이블을 두 개 만들었어요. config 테이블에 PIN을 저장하고, accounts 테이블에 계정 정보를 저장하는 구조예요. GAS처럼 서버가 콜드 스타트할 일이 없으니까 속도도 훨씬 빠르고, GAS URL 막히는 문제도 없어요.
PWA로 만들면 홈 화면에 아이콘으로 추가해서 앱처럼 전체화면으로 실행할 수 있어요. 주소창도 없고, 딱 앱처럼 보이거든요. 여기에 서비스워커로 앱 파일을 캐싱해두면, 데이터는 Supabase에서 불러오더라도 UI는 즉시 뜨는 구조가 됩니다.
* 노인 친화적 UI를 만드는 게 제일 어려웠다.
기능보다 UI가 더 힘들었어요. 아버지 폰이 갤럭시 S24인데, 같은 CSS 수치가 아이폰이랑 안드로이드에서 완전히 다르게 보이거든요. 안드로이드는 화면 DPI가 높아서 CSS px이 실제로 더 크게 보여요. viewport에 width=390을 고정해서 해결했습니다.
"글씨 더 키워줘"를 수도 없이 반복했어요. 카드 사이트명 32px, 아이디 미리보기 22px, 펼쳤을 때 아이디·비번 30px까지 키웠는데도 "아직도 작아"였거든요. 결국 핵심은 글씨 크기가 아니라 카드 하나의 높이를 충분히 키워서 터치 영역을 넓히는 것이었어요. 카드 높이를 96px로 고정하고 나서야 "이 정도면 되겠다"는 반응이 나왔습니다.
PIN 화면도 마찬가지예요. 숫자 버튼을 빠르게 누르면 인식이 안 된다는 문제가 있었어요. 더블탭 확대를 막으려고 300ms 이내 두 번 탭을 차단하는 코드를 넣었는데, 이게 빠른 PIN 입력도 함께 막고 있던 거예요. 이 코드를 제거하고 CSS의 touch-action: manipulation으로 대체해서 해결했어요. 확대는 막으면서 빠른 탭은 살리는 방법이거든요.
* 속도 문제, 이렇게 해결했다.
PWA로 바꿨는데도 처음 로딩이 느리다는 피드백이 있었어요. 원인을 보니까 Supabase에서 PIN 확인하고 → 계정 목록 불러오는 게 순차적으로 일어나고 있었던 거예요. 이걸 PIN 입력 화면이 뜨는 순간 계정 목록을 미리 백그라운드에서 받아두는 방식으로 바꿨어요. PIN을 맞게 입력하면 이미 데이터가 준비돼 있으니까 거의 즉시 메인 화면으로 넘어가는 거죠.
로컬 캐시도 활용했어요. localStorage에 마지막으로 받아온 계정 목록을 저장해두고, 앱을 열면 일단 로컬 데이터를 먼저 보여준 뒤 Supabase에서 최신 데이터를 받아서 갱신하는 방식이에요. 체감 속도가 확 달라집니다.
* 최종 완성된 기능들.
| 기능 | 설명 |
|---|---|
| PIN 4자리 잠금 | Supabase에 저장, 어떤 기기에서든 동일한 PIN |
| 타입별 탭 | 전체·ID/PW·은행·카드·신분증으로 분류 |
| 은행 계좌 저장 | 은행명·계좌번호·비밀번호 |
| 카드 정보 저장 | 카드사·카드번호·비밀번호·유효기간 |
| 신분증 사진 보관 | Supabase Storage에 이미지 업로드 |
| 복사 버튼 | 아이디·비밀번호 한 번에 클립보드 복사 |
| 일괄 삭제 | 선택 모드에서 여러 항목 동시 삭제 |
| 검색 | 사이트명 실시간 검색 |
| PC↔폰 동기화 | 어디서 추가해도 Supabase에서 동기화 |
PC에서 제가 계정을 추가해드리면 아버지 폰에서 바로 보이는 구조라, 초기 데이터 세팅은 제가 다 해드렸어요. 18개 계정을 Supabase SQL로 한 번에 입력했습니다.
* AI랑 개발하면 어떤 점이 좋고 어떤 점이 힘든가.
1. 좋은 점.
코드를 몰라도 기능을 설명하면 만들어줘요. "복사 버튼 누르면 2초 후에 원래 텍스트로 돌아오게 해줘" 같은 요구를 그냥 말로 하면 돼요. 에러가 나도 에러 화면을 캡처해서 보내면 원인을 짚어주거든요. 개발 속도가 혼자 하는 것보다 압도적으로 빨라요.
2. 힘든 점.
코드를 이해 못하면 에러 상황에서 판단이 어렵다는 게 제일 힘들었어요. "이 오류가 왜 나는 거지?"를 파악하려면 어느 정도 코드를 읽을 수 있어야 하는데, 처음엔 그게 전혀 안 됐거든요. 그냥 에러 화면 캡처해서 물어보는 수밖에 없었는데, 그러다 보면 문맥이 끊겨서 엉뚱한 방향으로 가는 경우도 있었어요. 지금은 에러 메시지를 어느 정도 읽을 수 있게 됐는데, 그게 이 과정에서 생긴 부수적인 수확이에요.
* 마무리 정리.
단순 HTML localStorage에서 시작해서, GAS + Google Sheets를 거쳐, 최종적으로 Supabase + GitHub Pages PWA까지 왔어요. 기술 스택이 세 번 바뀐 셈인데, 돌아보면 각 단계에서 한계를 직접 부딪혀봐야 다음 방향이 보였던 것 같아요. 처음부터 "PWA로 가야겠다"는 생각은 없었거든요.
아버지가 "이거 편하네"라고 한마디 하셨을 때, 그게 제일 뿌듯했어요. 쿠팡 비밀번호 물어보는 전화가 줄었냐고요? 확실히 줄었습니다. 아직 가끔은 오지만요. 😄
본 글은 개인 개발 경험을 기록한 것으로, 특정 서비스의 광고가 아닙니다.