@@ -0,0 +1,100 @@
|
|||||||
|
# 돈돼지 가계부 — Play 스토어 첫 출시 진행 상황
|
||||||
|
|
||||||
|
> 최종 업데이트: 2026-06-28. 첫 정식 출시(무료 앱) 준비 로그 + 남은 로드맵.
|
||||||
|
> 관련 문서: [play-store-listing.md](play-store-listing.md) · [production-access-draft.md](production-access-draft.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ 완료된 작업
|
||||||
|
|
||||||
|
### 출시 전략 결정
|
||||||
|
- **무료 앱**으로 첫 출시 (수익화 기능은 순차 도입)
|
||||||
|
- 첫 출시 기능 플래그 (`src/config/features.js`, 전부 `false`):
|
||||||
|
- `BILLING_ENABLED=false` — 인앱결제 스텁이라 결제 없이 프리미엄 부여되는 악용 차단
|
||||||
|
- `ADS_ENABLED=false` — 애드핏 미승인 + 데이터 안전 양식 단순화(광고 식별자 미수집)
|
||||||
|
- `CARD_NOTIF_ENABLED=false` — NotificationListenerService 정책 심사 회피
|
||||||
|
- 패키지명 `kr.sblog.slimbudget` **유지**(출시 후 영구 고정. 사용자 비노출이라 무방)
|
||||||
|
|
||||||
|
### 인프라 / 정책 페이지
|
||||||
|
- **www.sblog.kr** 도메인 + 인증서 확장 (app.sblog.kr와 동일 dist 서빙)
|
||||||
|
- 공개 URL: `/privacy`, `/terms`, `/account-deletion` (모두 200 확인)
|
||||||
|
- 개인정보처리방침 광고 문구 → "현재 광고 없음 + 향후 도입 가능" 미래형으로 정합화
|
||||||
|
|
||||||
|
### 빌드 / 서명
|
||||||
|
- **AAB**: `android/app/build/outputs/bundle/release/app-release.aab`
|
||||||
|
- targetSdk 36, versionCode 1, versionName 1.0
|
||||||
|
- 업로드 키(CN=Don Dwaeji) 서명 확인 (디버그 아님)
|
||||||
|
- 결제·광고·카드알림 전부 OFF 클린 (NotificationListener 권한 없음 검증)
|
||||||
|
- 키스토어: `android/upload-keystore.jks` + `android/key.properties` (gitignore, **이 PC에만 존재 — 분실 시 업데이트 불가, 반드시 백업**)
|
||||||
|
|
||||||
|
### 스토어 그래픽 (`brand/store/`)
|
||||||
|
- `icon-512.png` (512×512 불투명)
|
||||||
|
- `feature-1024x500.png` / `feature-1024x500-text.png` (피처그래픽, 택1)
|
||||||
|
- 생성기 `brand/gen-store-assets.mjs` + 마스터 `brand/logo-1024.png`
|
||||||
|
|
||||||
|
### 스크린샷 (`brand/screenshots/`, 7컷, 실기기 1080×2340)
|
||||||
|
- shot0 홈 대시보드(리드) / shot1 가계부 내역 / shot2 통계 / shot3 고정지출 / shot4 계좌 / shot5 게시판 / shot6 게시글 상세
|
||||||
|
|
||||||
|
### 등록 문서
|
||||||
|
- `brand/play-store-listing.md` — 등록정보(제목·설명)·데이터안전 양식·콘텐츠등급 답안
|
||||||
|
- `brand/production-access-draft.md` — 프로덕션 액세스 신청서 초안(4문항)
|
||||||
|
|
||||||
|
### 코드 수정 (배포 완료)
|
||||||
|
- 인앱결제 진입점 비활성화 (UpgradeView "곧 출시 예정", AccountInfoView 정기결제 카드 숨김)
|
||||||
|
- 광고 비활성화 (AdBanner + App.vue 게이팅, 하단 광고 여백 제거)
|
||||||
|
- 둘러보기(데모) 모드 메뉴 접근 버그 수정 (AppHeader 햄버거 `demo.on` 노출)
|
||||||
|
- 계정삭제 공개 페이지 추가
|
||||||
|
|
||||||
|
### 테스트 데이터 (캡처용, `sb_bt/scripts/`, gitignore 로컬 전용)
|
||||||
|
- 테스트 계정 `kenneth73510@gmail.com` (DB로 PREMIUM 부여)
|
||||||
|
- `seed-test-kenneth.sql`(계정+가계부), `seed-test-board.sql`(게시판 더미)
|
||||||
|
- `cleanup-test-board.sql`(게시판 더미 삭제 — **캡처 후 정리 완료**)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚧 현재 상태 (블로커)
|
||||||
|
- **개인 개발자 계정 신원확인 대기 중** — 앱 생성 버튼 잠김
|
||||||
|
- 주소 증빙서류 제출 이슈: 한국 서류 이름 마스킹/주소 미표기 → 도시가스/카드/은행창구 등 무마스킹 서류 확보 중
|
||||||
|
- 승인 소요: 보통 2~3 영업일, 길면 1~2주
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 남은 로드맵
|
||||||
|
|
||||||
|
1. **신원확인 승인** (대기) → "앱 만들기" 활성화
|
||||||
|
2. **앱 만들기** — 이름 "돈돼지 가계부", 한국어, 무료
|
||||||
|
3. **익명 셋업** (아래 체크리스트)
|
||||||
|
4. **필수 설정** — 데이터안전·콘텐츠등급·앱액세스(둘러보기 안내)·광고 없음·금융기능 선언·방침 URL (자료: play-store-listing.md)
|
||||||
|
5. **스토어 등록정보** — 문구 + 아이콘512·피처·스크린샷7
|
||||||
|
6. **비공개 테스트 트랙** — AAB 업로드 + **테스터 12명 opt-in**
|
||||||
|
7. **14일 연속** 12명 유지 → **프로덕션 액세스 신청** (자료: production-access-draft.md)
|
||||||
|
8. 승인 → **프로덕션 출시**
|
||||||
|
|
||||||
|
### ⚠️ 폐쇄 테스트 의무 (개인 계정 + 2023-11-13 이후 개설)
|
||||||
|
- **테스터 12명 이상 · 14일 연속** 폐쇄 테스트 후 프로덕션 신청 가능
|
||||||
|
- 실기기·실계정 필수 (에뮬·봇·중복 불인정)
|
||||||
|
- 14일 카운트 = "릴리스 승인 + 12명 opt-in" 둘 다 충족 시점부터
|
||||||
|
- 테스터 확보: 지인 비공개 방침 → 품앗이 커뮤니티(무료·안전) 또는 평판 좋은 유료 서비스(실사용 보장형만)
|
||||||
|
|
||||||
|
### 익명 셋업 체크리스트
|
||||||
|
- [ ] 전용 Gmail 생성 (개발자 연락처 + 품앗이용)
|
||||||
|
- [ ] 공개 개발자 이름 → 브랜드명 (법적이름은 구글 내부 전용, 비공개)
|
||||||
|
- [ ] 연락처 이메일(계정+앱) → 전용 메일
|
||||||
|
- [ ] 거래자 상태 → **비거래자(Non-trader)** (주소 비공개)
|
||||||
|
- [ ] 품앗이 노출은 전용 Gmail로만
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💡 출시 전 다듬기 후보 (대기 시간 활용, 선택)
|
||||||
|
신규(빈손) 유저 경험 위주 — 저위험·고효율 순:
|
||||||
|
1. **빈 상태 → 행동 유도** (첫 내역/계좌 추가 CTA) ⭐ 임팩트 高/노력 低
|
||||||
|
2. **오프라인·네트워크 오류 UX 견고화** (폐쇄 테스트 대비)
|
||||||
|
3. 첫 실행 온보딩(시작하기 카드)
|
||||||
|
4. Play 인앱 리뷰 요청
|
||||||
|
5. (선택) 앱 잠금(PIN/생체) — 매니페스트에 USE_BIOMETRIC 권한 존재, 구현 여부 미확인
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 다음 세션 재개 지점
|
||||||
|
**신원확인 승인 여부 확인** → 승인됐으면 "앱 만들기 → 익명 셋업 → 필수설정/등록정보 입력" 진행.
|
||||||
|
대기 중이면 **테스터 12명 Gmail 확보** + **출시 전 다듬기(빈 상태/오프라인)** 진행 권장.
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
# Play Console 등록 자료 — 돈돼지 가계부
|
||||||
|
|
||||||
|
> 첫 출시(무료 앱, 인앱결제·광고 모두 비활성화) 기준. Play Console 각 항목에 그대로 붙여넣기.
|
||||||
|
> 광고(카카오 애드핏)를 껐으므로 광고 식별자 수집/제3자 공유가 없어 데이터 안전이 단순함.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 스토어 등록정보 (Main store listing)
|
||||||
|
|
||||||
|
### 앱 이름 (30자 이내)
|
||||||
|
```
|
||||||
|
돈돼지 가계부
|
||||||
|
```
|
||||||
|
|
||||||
|
### 간단한 설명 / Short description (80자 이내)
|
||||||
|
```
|
||||||
|
가계부·자산·예산을 슬림하게. 고정지출 자동, 통계 차트, 영수증 인식까지.
|
||||||
|
```
|
||||||
|
|
||||||
|
### 자세한 설명 / Full description (4000자 이내)
|
||||||
|
```
|
||||||
|
🐷 돈돼지 가계부 — 슬림하게 관리하는 가계부 · 자산 · 예산
|
||||||
|
|
||||||
|
복잡한 가계부는 그만! 수입·지출을 빠르게 기록하고, 자산과 예산을 한눈에 관리하세요.
|
||||||
|
군더더기 없는 깔끔한 화면으로 매일 쓰기 부담 없는 가계부입니다.
|
||||||
|
|
||||||
|
▪ 간편한 가계부
|
||||||
|
· 수입·지출·이체를 몇 번의 터치로 기록
|
||||||
|
· 자주 쓰는 내역을 칩으로 등록해 1초 입력
|
||||||
|
· 월별 수입/지출/잔액 요약과 일별 내역 정리
|
||||||
|
|
||||||
|
▪ 자산 · 부채 관리
|
||||||
|
· 은행·현금·신용/체크카드·대출·투자 계좌를 한 곳에서
|
||||||
|
· 총자산 · 총부채 · 순자산을 자동 집계
|
||||||
|
|
||||||
|
▪ 고정 지출 자동 반영 (프리미엄)
|
||||||
|
· 월세·구독료·급여 등 반복 거래를 등록해두면 주기에 맞춰 자동 생성
|
||||||
|
· 실제 결제만 '확인'으로 확정 — 빠뜨림 없는 가계부
|
||||||
|
|
||||||
|
▪ 통계와 예산 (프리미엄)
|
||||||
|
· 분류별 지출, 수입 대비 지출, 지난달 대비, 월말 예상까지 차트로 분석
|
||||||
|
· 카테고리별 예산을 정하고 예산 대비 지출을 한눈에
|
||||||
|
|
||||||
|
▪ 영수증 인식 (프리미엄)
|
||||||
|
· 영수증을 촬영하면 금액·내역을 자동 인식해 입력 시간을 단축
|
||||||
|
|
||||||
|
▪ 커뮤니티
|
||||||
|
· 짠테크·재테크 노하우를 나누는 게시판
|
||||||
|
· 추천·댓글로 소통하고 좋은 글을 발견
|
||||||
|
|
||||||
|
▪ 그 외
|
||||||
|
· 다크 모드 지원
|
||||||
|
· 엑셀 백업/복구로 내 데이터를 안전하게 (프리미엄)
|
||||||
|
· 로그인 없이 '둘러보기'로 먼저 체험
|
||||||
|
|
||||||
|
무료로 핵심 가계부 기능을 모두 사용할 수 있으며, 통계·예산·고정지출 자동화·영수증 인식·백업 등은 프리미엄에서 제공됩니다. (구독 기능은 순차적으로 제공될 예정입니다.)
|
||||||
|
|
||||||
|
오늘부터 돈돼지와 함께 슬림한 가계 관리를 시작해보세요!
|
||||||
|
|
||||||
|
문의: sbsb401824@gmail.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### 기타 등록정보 필드
|
||||||
|
- **앱 카테고리**: 금융 (Finance)
|
||||||
|
- **태그**: 가계부, 예산, 자산관리
|
||||||
|
- **연락처 이메일**: sbsb401824@gmail.com
|
||||||
|
- **개인정보처리방침 URL**: https://www.sblog.kr/privacy
|
||||||
|
- **그래픽 에셋**:
|
||||||
|
- 앱 아이콘: brand/store/icon-512.png
|
||||||
|
- 그래픽 이미지(피처): brand/store/feature-1024x500-text.png (또는 -text 없는 버전)
|
||||||
|
- 휴대전화 스크린샷: brand/screenshots/shot1~6 (6장)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 데이터 안전 (Data safety) 양식
|
||||||
|
|
||||||
|
> "앱 콘텐츠 → 데이터 보안" 에서 작성. 아래 기준으로 답하면 개인정보처리방침과 일치함.
|
||||||
|
|
||||||
|
### 개요 질문
|
||||||
|
- 데이터를 수집하나요? → **예**
|
||||||
|
- 데이터를 제3자와 공유하나요? → **아니요** (광고 끔. OCR은 처리 위탁이며 별도 공유 아님)
|
||||||
|
- 전송 중 데이터가 암호화되나요? → **예** (HTTPS)
|
||||||
|
- 사용자가 데이터 삭제를 요청할 수 있나요? → **예** (앱 내 '회원 탈퇴' + 이메일 요청, https://www.sblog.kr/account-deletion)
|
||||||
|
|
||||||
|
### 수집하는 데이터 유형 (각 항목: 수집됨 / 목적 / 공유 여부)
|
||||||
|
|
||||||
|
| 데이터 유형 | 수집 | 공유 | 목적 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **개인 정보 → 이름** | 예 | 아니요 | 앱 기능, 계정 관리 |
|
||||||
|
| **개인 정보 → 이메일 주소** | 예 | 아니요 | 앱 기능, 계정 관리 |
|
||||||
|
| **금융 정보 → 기타 금융 정보** (사용자가 입력한 수입·지출·자산 등 가계부 데이터) | 예 | 아니요 | 앱 기능 |
|
||||||
|
| **사진 및 동영상 → 사진** (프로필·영수증·게시판 이미지) | 예 | 아니요 | 앱 기능 |
|
||||||
|
| **앱 활동 → 사용자가 생성한 콘텐츠** (게시판 글·댓글) | 예 | 아니요 | 앱 기능 |
|
||||||
|
| **앱 활동 → 앱 상호작용** | 예 | 아니요 | 앱 기능, 부정 이용 방지 |
|
||||||
|
|
||||||
|
각 항목 공통:
|
||||||
|
- **수집이 필수인가요?** → "데이터 수집이 필수임" (서비스 제공에 필요)
|
||||||
|
- **데이터가 암호화되나요(전송 중)?** → 예
|
||||||
|
- **삭제 요청 가능?** → 예
|
||||||
|
|
||||||
|
> ✅ 광고를 껐으므로 **광고 식별자(기기 ID) 수집/공유가 없습니다** — 데이터 안전 양식이 단순해지고 심사 불일치 리스크가 사라졌습니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 콘텐츠 등급 설문 (IARC)
|
||||||
|
|
||||||
|
> "앱 콘텐츠 → 콘텐츠 등급" 설문. 핵심 답안:
|
||||||
|
|
||||||
|
- 앱 카테고리: **유틸리티/생산성/기타** (또는 금융)
|
||||||
|
- 폭력/성적/혐오/약물/도박 콘텐츠: **모두 없음**
|
||||||
|
- **사용자 간 상호작용/사용자 생성 콘텐츠(게시판)**: **있음** → "사용자가 콘텐츠를 만들고 공유할 수 있음" 으로 정직하게 표시
|
||||||
|
- 신고 기능(5건 누적 블라인드)·관리자 차단 등 **중재 수단 보유** → 관련 질문에 "콘텐츠 신고/차단 기능 있음"으로 답변
|
||||||
|
- 위치 공유 / 개인정보 공유 기능: **없음**
|
||||||
|
- 디지털 구매(인앱결제): 첫 출시는 **비활성화** → "없음" (추후 활성화 시 등급 갱신)
|
||||||
|
- **광고 포함 여부**: **아니요** (광고 껐음 — 추후 활성화 시 갱신)
|
||||||
|
- 예상 등급: 대체로 **만 3세 이상(전체이용가)** 수준, UGC로 인해 일부 지역에서 Teen 가능 — 설문 결과 따름
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 기타 출시 전 체크
|
||||||
|
- **앱 액세스 권한**: 로그인 필요 → 심사용 테스트 계정 제공 (Play Console "앱 액세스 권한"에 kenneth73510 계정 또는 데모 안내). 데모(둘러보기)로도 핵심 화면 확인 가능함을 메모.
|
||||||
|
- **타겟 출시 국가**: 대한민국 (필요 시 확대)
|
||||||
|
- **데이터 삭제 URL**: https://www.sblog.kr/account-deletion (데이터 안전 양식 + 앱 콘텐츠)
|
||||||
|
- **앱 카테고리/연락처/방침 URL** 위 1번 참고
|
||||||
|
- 광고 포함 여부 질문: **아니요(광고 없음)** — 광고를 껐으므로 "광고 없음"으로 표시
|
||||||
|
- ⚠️ **개인정보처리방침 정합성**: 현재 방침 4항이 "무료 회원에게 광고가 표시되며…"로 적혀 있어 광고 OFF 와 불일치. 심사자 교차확인 대비해 방침의 광고 문구를 "향후 광고가 도입될 수 있으며…"(미래형)로 완화 권장. (원하면 수정·배포 도와드림)
|
||||||
|
```
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# 프로덕션 액세스 신청서 초안 — 돈돼지 가계부
|
||||||
|
|
||||||
|
> 14일 폐쇄 테스트 완료 후 Play Console "프로덕션 액세스 신청" 폼에 입력.
|
||||||
|
> ⚠️ [대괄호] 부분은 실제 테스트 후 본인 경험으로 교체하세요. 거짓·과장 금지 — 실제로 테스트한 내용을 적어야 합니다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Q1. 테스터를 어떻게 모집했나요? (How did you recruit your testers?)
|
||||||
|
|
||||||
|
```
|
||||||
|
실제 Android 기기와 Google 계정을 보유한 [12]명의 테스터를 모집해 비공개 테스트를 진행했습니다.
|
||||||
|
테스터는 안드로이드 사용자 지인 및 개발자 커뮤니티의 베타 테스터 모임을 통해 모았으며,
|
||||||
|
모두 실제 기기에 앱을 설치하고 14일간 사용했습니다.
|
||||||
|
```
|
||||||
|
> 유료/품앗이 서비스를 쓴 경우에도 "실제 기기·실제 계정 테스터"라는 사실 위주로 적되, 허위로 쓰지 마세요.
|
||||||
|
|
||||||
|
## Q2. 테스터와 어떻게 소통하고 피드백을 수집했나요?
|
||||||
|
|
||||||
|
```
|
||||||
|
이메일과 메신저(오픈채팅)로 테스터에게 설치 안내와 점검 항목을 전달했습니다.
|
||||||
|
점검 항목: 회원가입/구글 로그인, 가계부 수입·지출 기록, 계좌·자산 관리, 예산·통계 화면,
|
||||||
|
고정지출 자동 반영, 커뮤니티 게시판(글·댓글·추천), 다크 모드.
|
||||||
|
테스터가 사용 중 발견한 버그·불편 사항을 회신받아 정리했습니다.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Q3. 어떤 피드백을 받았고, 무엇을 개선했나요?
|
||||||
|
|
||||||
|
```
|
||||||
|
주요 피드백과 개선:
|
||||||
|
- 둘러보기(로그인 없이 체험) 모드에서 일부 화면의 메뉴 접근이 불편 → 메뉴 진입 동선 수정
|
||||||
|
- 특정 기기에서 한글 입력 시 자동완성/맞춤법 검사로 입력이 끊김 → 입력 필드 설정 수정으로 해결
|
||||||
|
- 무료/유료 기능 구분 및 화면 흐름이 헷갈린다는 의견 → 안내 문구·접근 동선 정리
|
||||||
|
- [본인 테스트 중 실제로 발견한 버그/불편과 그에 대한 수정 내용을 추가 기재]
|
||||||
|
크래시나 데이터 유실 등 치명적 문제는 발견되지 않았습니다.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Q4. 앱이 프로덕션에 준비되었다고 보는 이유는?
|
||||||
|
|
||||||
|
```
|
||||||
|
돈돼지 가계부는 웹·Windows 데스크톱에서 이미 운영 중인 개인 가계부 서비스이며,
|
||||||
|
이번 비공개 테스트로 Android 실기기에서 핵심 기능(가계부 기록, 계좌·자산, 예산·통계,
|
||||||
|
고정지출 자동 반영, 커뮤니티, 구글 로그인)의 안정성을 14일간 검증했습니다.
|
||||||
|
다양한 기기에서 크래시 없이 정상 동작하며, 테스트 중 제기된 개선 사항을 반영했습니다.
|
||||||
|
개인정보처리방침·데이터 삭제 안내 등 정책 요건도 갖추어 정식 출시 준비가 되었다고 판단합니다.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## (선택) 영어 버전 — 폼이 영어로 표시될 경우
|
||||||
|
|
||||||
|
**Q1 Recruit:** I recruited [12] testers who own real Android devices with genuine Google accounts, through acquaintances and a developer beta-tester community. All installed the app and used it over the 14-day period.
|
||||||
|
|
||||||
|
**Q2 Feedback collection:** I communicated via email and messenger, sharing install instructions and a checklist (Google sign-in, expense/income tracking, accounts & assets, budget & statistics, recurring transactions, community board, dark mode), and collected bug reports and usability feedback.
|
||||||
|
|
||||||
|
**Q3 Findings & changes:** Key fixes included improving menu navigation in the guest "explore" mode, resolving a Korean-input (IME) issue on certain devices, and clarifying free/premium feature flow. [Add the real bugs you found.] No crashes or data-loss issues were found.
|
||||||
|
|
||||||
|
**Q4 Readiness:** Don Dwaeji budget app already runs in production on web and Windows desktop. The closed test validated the Android build's core features on real devices over 14 days with no crashes, and feedback was addressed. Privacy policy and data-deletion pages are in place, so the app is ready for production.
|
||||||
|
After Width: | Height: | Size: 174 KiB |
|
After Width: | Height: | Size: 183 KiB |
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 184 KiB |
|
After Width: | Height: | Size: 111 KiB |
|
After Width: | Height: | Size: 144 KiB |
|
After Width: | Height: | Size: 166 KiB |
@@ -39,6 +39,21 @@ const budgetPct = computed(() =>
|
|||||||
const budgetLeft = computed(() => budgetTotal.value - spentTotal.value)
|
const budgetLeft = computed(() => budgetTotal.value - spentTotal.value)
|
||||||
const budgetOver = computed(() => budgetLeft.value < 0)
|
const budgetOver = computed(() => budgetLeft.value < 0)
|
||||||
|
|
||||||
|
// ===== 신규 유저 시작하기 안내 =====
|
||||||
|
// 데이터가 전혀 없으면(이번 달 내역 0 + 자산/부채 0) 첫 사용 가이드를 노출.
|
||||||
|
// 한 번 닫으면 다시 보이지 않도록 로컬에 기록.
|
||||||
|
const isEmpty = computed(() =>
|
||||||
|
entries.value.length === 0 &&
|
||||||
|
networth.value.totalAssets === 0 &&
|
||||||
|
networth.value.totalLiabilities === 0,
|
||||||
|
)
|
||||||
|
const gsDismissed = ref(localStorage.getItem('home:gettingStarted:dismissed') === '1')
|
||||||
|
const showGettingStarted = computed(() => !loading.value && !error.value && isEmpty.value && !gsDismissed.value)
|
||||||
|
function dismissGettingStarted() {
|
||||||
|
gsDismissed.value = true
|
||||||
|
try { localStorage.setItem('home:gettingStarted:dismissed', '1') } catch { /* 무시 */ }
|
||||||
|
}
|
||||||
|
|
||||||
function won(n) {
|
function won(n) {
|
||||||
return (n ?? 0).toLocaleString('ko-KR')
|
return (n ?? 0).toLocaleString('ko-KR')
|
||||||
}
|
}
|
||||||
@@ -166,6 +181,30 @@ onMounted(load)
|
|||||||
|
|
||||||
<p v-if="error" class="msg error">{{ error }}</p>
|
<p v-if="error" class="msg error">{{ error }}</p>
|
||||||
|
|
||||||
|
<!-- 신규 유저 시작하기 안내 (데이터 없을 때만) -->
|
||||||
|
<section v-if="showGettingStarted" class="getting-started">
|
||||||
|
<button type="button" class="gs-close" aria-label="안내 닫기" @click="dismissGettingStarted">×</button>
|
||||||
|
<h2 class="gs-title">가계부를 시작해볼까요? 🐷</h2>
|
||||||
|
<p class="gs-sub">아래 순서로 첫 기록을 시작해보세요.</p>
|
||||||
|
<div class="gs-steps">
|
||||||
|
<RouterLink to="/account/wallets" class="gs-step">
|
||||||
|
<span class="gs-num">1</span>
|
||||||
|
<span class="gs-text"><b>계좌 등록하기</b><span>은행·카드·현금 추가</span></span>
|
||||||
|
<span class="gs-arrow">→</span>
|
||||||
|
</RouterLink>
|
||||||
|
<RouterLink to="/account/categories" class="gs-step">
|
||||||
|
<span class="gs-num">2</span>
|
||||||
|
<span class="gs-text"><b>기본 분류 불러오기</b><span>식비·교통 등 카테고리 준비</span></span>
|
||||||
|
<span class="gs-arrow">→</span>
|
||||||
|
</RouterLink>
|
||||||
|
<RouterLink to="/account/entries" class="gs-step">
|
||||||
|
<span class="gs-num">3</span>
|
||||||
|
<span class="gs-text"><b>첫 내역 기록하기</b><span>수입·지출을 추가</span></span>
|
||||||
|
<span class="gs-arrow">→</span>
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<div class="cards">
|
<div class="cards">
|
||||||
<!-- 이번 달 수입/지출/수지 -->
|
<!-- 이번 달 수입/지출/수지 -->
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@@ -346,6 +385,90 @@ onMounted(load)
|
|||||||
opacity: 0.65;
|
opacity: 0.65;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
/* 신규 유저 시작하기 안내 */
|
||||||
|
.getting-started {
|
||||||
|
position: relative;
|
||||||
|
margin: 1.2rem 0;
|
||||||
|
padding: 1.1rem 1.2rem 1.2rem;
|
||||||
|
border: 1px solid hsla(160, 100%, 37%, 0.35);
|
||||||
|
border-radius: 14px;
|
||||||
|
background: hsla(160, 100%, 37%, 0.06);
|
||||||
|
}
|
||||||
|
.gs-close {
|
||||||
|
position: absolute;
|
||||||
|
top: 0.5rem;
|
||||||
|
right: 0.6rem;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-text);
|
||||||
|
opacity: 0.5;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.gs-close:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
.gs-title {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: var(--color-heading);
|
||||||
|
}
|
||||||
|
.gs-sub {
|
||||||
|
margin-top: 0.25rem;
|
||||||
|
font-size: 0.86rem;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
.gs-steps {
|
||||||
|
margin-top: 0.9rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
.gs-step {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.7rem;
|
||||||
|
padding: 0.7rem 0.85rem;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: var(--color-background);
|
||||||
|
color: var(--color-text);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.gs-step:hover {
|
||||||
|
border-color: hsla(160, 100%, 37%, 0.6);
|
||||||
|
}
|
||||||
|
.gs-num {
|
||||||
|
flex: none;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: hsla(160, 100%, 37%, 1);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 0.82rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.gs-text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.gs-text b {
|
||||||
|
font-size: 0.92rem;
|
||||||
|
}
|
||||||
|
.gs-text span {
|
||||||
|
font-size: 0.78rem;
|
||||||
|
opacity: 0.65;
|
||||||
|
}
|
||||||
|
.gs-arrow {
|
||||||
|
margin-left: auto;
|
||||||
|
color: hsla(160, 100%, 37%, 1);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
.cards {
|
.cards {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
|
|||||||
@@ -954,7 +954,13 @@ onMounted(async () => {
|
|||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<p v-else-if="!loading" class="msg">{{ hasFilter ? '조건에 맞는 내역이 없습니다.' : '이 달의 내역이 없습니다.' }}</p>
|
<p v-else-if="!loading && hasFilter" class="msg">조건에 맞는 내역이 없습니다.</p>
|
||||||
|
<div v-else-if="!loading" class="empty-state">
|
||||||
|
<p class="empty-emoji">📒</p>
|
||||||
|
<p class="empty-title">아직 기록된 내역이 없어요</p>
|
||||||
|
<p class="empty-desc">첫 수입·지출을 기록해보세요.</p>
|
||||||
|
<button type="button" class="empty-cta" @click="openCreate">+ 첫 내역 추가</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 추가/수정 모달 -->
|
<!-- 추가/수정 모달 -->
|
||||||
<Teleport to="body">
|
<Teleport to="body">
|
||||||
@@ -1515,6 +1521,39 @@ button.primary {
|
|||||||
.msg {
|
.msg {
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
}
|
}
|
||||||
|
/* 신규 유저 빈 상태 */
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2.4rem 1rem 2rem;
|
||||||
|
}
|
||||||
|
.empty-emoji {
|
||||||
|
font-size: 2.6rem;
|
||||||
|
}
|
||||||
|
.empty-title {
|
||||||
|
margin-top: 0.6rem;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--color-heading);
|
||||||
|
}
|
||||||
|
.empty-desc {
|
||||||
|
margin-top: 0.3rem;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
.empty-cta {
|
||||||
|
margin-top: 1.1rem;
|
||||||
|
padding: 0.7rem 1.6rem;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: hsla(160, 100%, 37%, 1);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.empty-cta:hover {
|
||||||
|
background: hsla(160, 100%, 32%, 1);
|
||||||
|
}
|
||||||
.msg.error {
|
.msg.error {
|
||||||
color: #c0392b;
|
color: #c0392b;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -421,7 +421,12 @@ onBeforeUnmount(() => sortable?.destroy())
|
|||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p v-if="!loading && !rows.length" class="msg">등록된 항목이 없습니다.</p>
|
<div v-if="!loading && !rows.length" class="empty-state">
|
||||||
|
<p class="empty-emoji">🏦</p>
|
||||||
|
<p class="empty-title">아직 등록된 계좌가 없어요</p>
|
||||||
|
<p class="empty-desc">은행·카드·현금·대출·투자를 추가해보세요.</p>
|
||||||
|
<button type="button" class="empty-cta" @click="openCreate">+ 계좌 추가</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 추가/수정 모달 -->
|
<!-- 추가/수정 모달 -->
|
||||||
<Teleport to="body">
|
<Teleport to="body">
|
||||||
@@ -795,6 +800,39 @@ button.primary {
|
|||||||
.msg {
|
.msg {
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
}
|
}
|
||||||
|
/* 신규 유저 빈 상태 */
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2.4rem 1rem 2rem;
|
||||||
|
}
|
||||||
|
.empty-emoji {
|
||||||
|
font-size: 2.6rem;
|
||||||
|
}
|
||||||
|
.empty-title {
|
||||||
|
margin-top: 0.6rem;
|
||||||
|
font-size: 1.05rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--color-heading);
|
||||||
|
}
|
||||||
|
.empty-desc {
|
||||||
|
margin-top: 0.3rem;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
.empty-cta {
|
||||||
|
margin-top: 1.1rem;
|
||||||
|
padding: 0.7rem 1.6rem;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: hsla(160, 100%, 37%, 1);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.empty-cta:hover {
|
||||||
|
background: hsla(160, 100%, 32%, 1);
|
||||||
|
}
|
||||||
.msg.error {
|
.msg.error {
|
||||||
color: #c0392b;
|
color: #c0392b;
|
||||||
}
|
}
|
||||||
|
|||||||