Merge branch 'dev'
Deploy / deploy (push) Failing after 15m24s

This commit is contained in:
ByungCheol
2026-06-28 17:09:37 +09:00
6 changed files with 207 additions and 20 deletions
+7 -2
View File
@@ -23,6 +23,8 @@ const router = useRouter()
const authed = computed(() => auth.isAuthenticated || demo.on) const authed = computed(() => auth.isAuthenticated || demo.on)
// 광고: 유료(PREMIUM) 회원에게는 표시하지 않음 // 광고: 유료(PREMIUM) 회원에게는 표시하지 않음
const showAds = computed(() => !auth.isPremium) const showAds = computed(() => !auth.isPremium)
// 공개 법적 고지(개인정보처리방침·약관)는 웹 브라우저에서도 접근 허용 (앱 마켓 정책 URL)
const isPublicLegal = computed(() => !!route.meta.public)
function leaveDemo() { function leaveDemo() {
exitDemo() exitDemo()
@@ -61,8 +63,11 @@ watch(() => route.fullPath, () => ui.closeSidebar())
</script> </script>
<template> <template>
<!-- (브라우저): 안내 페이지 --> <!-- (브라우저): 공개 법적 고지는 노출, 외엔 안내 페이지 -->
<WebOnlyNotice v-if="!isApp" /> <template v-if="!isApp">
<RouterView v-if="isPublicLegal" />
<WebOnlyNotice v-else />
</template>
<!-- (Capacitor): 전체 기능 --> <!-- (Capacitor): 전체 기능 -->
<template v-else> <template v-else>
+21
View File
@@ -1,4 +1,5 @@
<script setup> <script setup>
import { RouterLink } from 'vue-router'
import AdBanner from '@/components/AdBanner.vue' import AdBanner from '@/components/AdBanner.vue'
// PC·모바일 웹에서 노출되는 안내 페이지. 실제 이용은 앱(모바일) 또는 PC 데스크톱 클라이언트에서. // PC·모바일 웹에서 노출되는 안내 페이지. 실제 이용은 앱(모바일) 또는 PC 데스크톱 클라이언트에서.
// PC 설치파일은 서버 /download/ 경로(영속 디렉터리)에서 서빙 — nginx 설정 참고. // PC 설치파일은 서버 /download/ 경로(영속 디렉터리)에서 서빙 — nginx 설정 참고.
@@ -38,6 +39,12 @@ const mobileReady = false
<li><span class="f-icon">🏦</span><b>자산·부채 관리</b><span>계좌·카드·대출·투자까지</span></li> <li><span class="f-icon">🏦</span><b>자산·부채 관리</b><span>계좌·카드·대출·투자까지</span></li>
<li><span class="f-icon">🎯</span><b>예산과 분석</b><span>예산 대비 지출·차트</span></li> <li><span class="f-icon">🎯</span><b>예산과 분석</b><span>예산 대비 지출·차트</span></li>
</ul> </ul>
<nav class="legal-links">
<RouterLink to="/privacy">개인정보처리방침</RouterLink>
<span aria-hidden="true">·</span>
<RouterLink to="/terms">이용약관</RouterLink>
</nav>
</div> </div>
<!-- 하단 광고 (유료 회원 제외) --> <!-- 하단 광고 (유료 회원 제외) -->
@@ -158,4 +165,18 @@ const mobileReady = false
font-size: 0.78rem; font-size: 0.78rem;
opacity: 0.65; opacity: 0.65;
} }
.legal-links {
margin-top: 1.4rem;
display: flex;
justify-content: center;
gap: 0.5rem;
font-size: 0.8rem;
opacity: 0.7;
}
.legal-links a {
color: var(--color-text);
}
.legal-links a:hover {
color: hsla(160, 100%, 37%, 1);
}
</style> </style>
+59 -18
View File
@@ -1,14 +1,17 @@
// 회원가입 약관 본문. 모달에서 '전문 보기'로 노출. // 이용약관 / 개인정보처리방침 본문. 가입 모달 '전문 보기' 및 /terms, /privacy 페이지에서 노출.
// 서비스명: 돈돼지 가계부 (가계부·자산·예산 관리) // 서비스명: 돈돼지 가계부 (가계부·자산·예산 관리)
export const SERVICE_NAME = '돈돼지 가계부' export const SERVICE_NAME = '돈돼지 가계부'
export const EFFECTIVE_DATE = '2026-06-28'
// 개인정보 보호 문의 — 운영자 연락처(필요 시 변경)
export const CONTACT_EMAIL = 'sbsb401824@gmail.com'
export const TERMS_OF_SERVICE = `제1조 (목적) export const TERMS_OF_SERVICE = `제1조 (목적)
이 약관은 ${SERVICE_NAME}(이하 "서비스")가 제공하는 가계부·자산·예산 관리 서비스의 이용과 관련하여 서비스와 회원 간의 권리, 의무 및 책임사항을 규정함을 목적으로 합니다. 이 약관은 ${SERVICE_NAME}(이하 "서비스")가 제공하는 가계부·자산·예산 관리 서비스의 이용과 관련하여 서비스와 회원 간의 권리, 의무 및 책임사항을 규정함을 목적으로 합니다.
제2조 (정의) 제2조 (정의)
1. "회원"이란 본 약관에 동의하고 서비스에 가입하여 이용하는 자를 말합니다. 1. "회원"이란 본 약관에 동의하고 서비스에 가입하여 이용하는 자를 말합니다.
2. "계정"이란 회원 식별과 서비스 이용을 위해 회원이 등록한 아이디와 비밀번호를 말합니다. 2. "계정"이란 회원 식별과 서비스 이용을 위해 회원이 등록(또는 소셜 로그인으로 연동)한 식별 정보를 말합니다.
제3조 (약관의 효력 및 변경) 제3조 (약관의 효력 및 변경)
1. 본 약관은 회원이 가입 시 동의함으로써 효력이 발생합니다. 1. 본 약관은 회원이 가입 시 동의함으로써 효력이 발생합니다.
@@ -26,33 +29,71 @@ export const TERMS_OF_SERVICE = `제1조 (목적)
제6조 (게시물) 제6조 (게시물)
회원이 게시판 등에 작성한 게시물의 권리와 책임은 작성자에게 있으며, 서비스 운영이나 타인의 권리를 침해하는 게시물은 사전 통지 없이 제한될 수 있습니다. 회원이 게시판 등에 작성한 게시물의 권리와 책임은 작성자에게 있으며, 서비스 운영이나 타인의 권리를 침해하는 게시물은 사전 통지 없이 제한될 수 있습니다.
제7조 (계약 해지) 제7조 (유료 서비스 및 결제)
회원은 언제든지 탈퇴를 요청할 수 있으며, 탈퇴 시 회원의 데이터는 관련 법령에서 정한 경우를 제외하고 지체 없이 삭제됩니다. 1. 서비스는 일부 기능을 유료(프리미엄) 구독으로 제공할 수 있습니다.
2. 모바일 앱의 결제는 Google Play 등 앱 마켓의 인앱결제를 통해 이루어지며, 결제·환불·정기결제(자동 갱신)의 처리 및 정책은 해당 마켓의 규정을 따릅니다.
3. 구독은 만료일까지 이용할 수 있으며, 해지하더라도 남은 기간 동안 이용이 유지된 후 무료로 전환됩니다.
4. 청약철회 및 환불은 관련 법령과 앱 마켓 정책에 따릅니다.
제8조 (면책) 제8조 (광고)
서비스는 천재지변, 회원의 귀책사유, 제3자 서비스(시세 제공처 등)의 장애 등 합리적으로 통제할 수 없는 사유로 인한 손해에 대하여 책임을 지지 않습니다. 서비스는 무료 회원에게 광고를 표시할 수 있으며, 유료(프리미엄) 회원에게는 광고를 표시하지 않습니다.
제9조 (계약 해지)
회원은 언제든지 앱 내 '회원 탈퇴' 기능으로 탈퇴할 수 있으며, 탈퇴 시 회원의 데이터는 관련 법령에서 정한 경우를 제외하고 지체 없이 삭제됩니다.
제10조 (면책)
서비스는 천재지변, 회원의 귀책사유, 제3자 서비스(시세 제공처, 앱 마켓, 광고 등)의 장애 등 합리적으로 통제할 수 없는 사유로 인한 손해에 대하여 책임을 지지 않습니다.
부칙 부칙
본 약관은 가입 시점부터 적용됩니다.` 본 약관은 ${EFFECTIVE_DATE}부터 적용됩니다.`
export const PRIVACY_POLICY = `${SERVICE_NAME}는 회원가입 및 서비스 제공을 위해 아래와 같이 개인정보를 수집·이용합니다. export const PRIVACY_POLICY = `${SERVICE_NAME}(이하 "서비스")는 이용자의 개인정보를 중요하게 생각하며, 「개인정보 보호법」 등 관련 법령을 준수합니다. 본 방침은 서비스가 어떤 정보를 어떻게 수집·이용·보관하는지 안내합니다.
1. 수집 항목 1. 수집하는 개인정보 항목
- 필수: 아이디, 비밀번호(암호화 저장), 이름 가) 회원가입·계정
- 선택: 이메일 - 로컬 가입: 아이디, 비밀번호(단방향 암호화 저장), 이름, 이메일(선택)
- 서비스 이용 과정에서 회원이 직접 입력하는 가계부·자산·거래 내역 등 - 구글 로그인: 구글 계정 식별자, 이름, 이메일, 프로필 사진(구글 제공 범위)
나) 서비스 이용 과정에서 회원이 직접 입력·등록하는 정보
- 가계부·계좌·자산·예산·거래 내역 등 가계부 데이터
- 게시판 글·댓글, 직접 업로드한 프로필 사진(선택)
다) 자동으로 생성·수집되는 정보
- 서비스 이용 기록, 기기·브라우저 정보(부정 이용 방지 및 광고 제공 목적)
- 인앱결제 시 구매 토큰 및 구독 상태(결제 검증·구독 관리 목적)
2. 수집·이용 목적 2. 수집·이용 목적
- 회원 식별 및 로그인 등 계정 관리 - 회원 식별 및 로그인 등 계정 관리
- 가계부·자산·예산 관리 기능 제공 및 회원 데이터 보관 - 가계부·자산·예산 관리 기능 제공 및 회원 데이터 보관
- 유료 구독 결제 검증 및 구독(자동 갱신·해지) 관리
- 무료 회원 대상 광고 제공
- 서비스 운영, 문의 응대 및 부정 이용 방지 - 서비스 운영, 문의 응대 및 부정 이용 방지
3. 보유 및 이용 기간 3. 처리위탁 및 제3자
- 회원 탈퇴 시까지 보유하며, 탈퇴 시 지체 없이 파기합니다. 서비스는 기능 제공을 위해 아래 사업자의 서비스를 이용하며, 필요한 범위에서 정보가 처리될 수 있습니다.
- Google: 구글 로그인(인증), 인앱결제(Google Play), 영수증 문자 인식(Google Cloud Vision OCR)
- Kakao(카카오): 광고 노출(카카오 애드핏) — 무료 회원에 한함
영수증 OCR은 회원이 촬영·선택한 영수증 이미지를 텍스트 인식 목적으로만 처리합니다. 서비스는 회원의 개인정보를 위 목적 외로 외부에 판매하지 않습니다.
4. 광고
- 무료 회원에게는 광고가 표시되며, 광고 식별·노출을 위해 기기 정보 등이 광고 사업자에 의해 처리될 수 있습니다.
- 유료(프리미엄) 회원에게는 광고가 표시되지 않습니다.
5. 결제 정보
- 결제는 Google Play 등 앱 마켓의 인앱결제로 처리되며, 카드번호 등 결제수단 정보는 서비스가 저장하지 않습니다.
- 서비스는 결제 검증·구독 관리를 위한 구매 토큰과 구독 상태(상품, 만료일 등)만 보관합니다.
6. 보유 및 이용 기간
- 회원 탈퇴 시까지 보유하며, 탈퇴 시 회원의 데이터(가계부·게시물·포인트·결제기록 등)를 지체 없이 파기합니다.
- 다만 관련 법령에 따라 보존이 필요한 경우 해당 기간 동안 보관합니다. - 다만 관련 법령에 따라 보존이 필요한 경우 해당 기간 동안 보관합니다.
4. 동의 거부 권리 및 불이익 7. 이용자의 권리
- 회원은 개인정보 수집·이용 동의를 거부할 권리가 있습니다. - 회원은 자신의 개인정보에 대해 열람·정정·삭제·처리정지를 요청할 수 있으며, 앱 내 '회원 탈퇴'로 계정과 데이터를 삭제할 수 있습니다.
- 다만 필수 항목에 동의하지 않을 경우 회원가입 및 서비스 이용이 제한됩니다. - 개인정보 수집·이용 동의를 거부할 권리가 있으나, 필수 항목 미동의 시 회원가입 및 서비스 이용이 제한됩니다.
비밀번호는 복호화가 불가능한 방식으로 암호화하여 저장되며, 운영자도 원문을 확인할 수 없습니다.` 8. 안전성 확보 조치
- 비밀번호는 복호화가 불가능한 방식으로 암호화하여 저장하며, 운영자도 원문을 확인할 수 없습니다.
- 전송 구간 암호화(HTTPS) 등 합리적인 보호조치를 적용합니다.
9. 개인정보 보호 문의
- 문의: ${CONTACT_EMAIL}
본 개인정보처리방침은 ${EFFECTIVE_DATE}부터 적용됩니다.`
+14
View File
@@ -31,6 +31,20 @@ const router = createRouter({
component: () => import('../views/UpgradeView.vue'), component: () => import('../views/UpgradeView.vue'),
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
{
// 개인정보처리방침 (공개 — 웹에서도 접근, 앱 마켓 정책 URL)
path: '/privacy',
name: 'privacy',
component: () => import('../views/LegalView.vue'),
meta: { public: true, doc: 'privacy' },
},
{
// 이용약관 (공개)
path: '/terms',
name: 'terms',
component: () => import('../views/LegalView.vue'),
meta: { public: true, doc: 'terms' },
},
{ {
path: '/users', path: '/users',
name: 'users', name: 'users',
+88
View File
@@ -0,0 +1,88 @@
<script setup>
import { computed } from 'vue'
import { RouterLink, useRoute, useRouter } from 'vue-router'
import { TERMS_OF_SERVICE, PRIVACY_POLICY, SERVICE_NAME } from '@/constants/terms'
const route = useRoute()
const router = useRouter()
const isPrivacy = computed(() => route.meta.doc === 'privacy')
const title = computed(() => (isPrivacy.value ? '개인정보처리방침' : '이용약관'))
const body = computed(() => (isPrivacy.value ? PRIVACY_POLICY : TERMS_OF_SERVICE))
function goBack() {
if (window.history.length > 1) router.back()
else router.push('/')
}
</script>
<template>
<main class="legal">
<header class="legal-head">
<div class="brand">{{ SERVICE_NAME }}</div>
<h1>{{ title }}</h1>
</header>
<pre class="legal-body">{{ body }}</pre>
<div class="legal-foot">
<RouterLink :to="isPrivacy ? '/terms' : '/privacy'" class="legal-link">
{{ isPrivacy ? '이용약관' : '개인정보처리방침' }} 보기
</RouterLink>
<button type="button" class="legal-back" @click="goBack">돌아가기</button>
</div>
</main>
</template>
<style scoped>
.legal {
max-width: 760px;
margin: 0 auto;
padding: 1.5rem;
}
.legal-head {
text-align: center;
margin-bottom: 1.25rem;
}
.brand {
font-size: 1.1rem;
font-weight: 700;
color: hsla(160, 100%, 37%, 1);
}
.legal-head h1 {
font-size: 1.4rem;
margin-top: 0.3rem;
}
.legal-body {
white-space: pre-wrap;
word-break: break-word;
font-family: inherit;
font-size: 0.9rem;
line-height: 1.7;
color: var(--color-text);
background: var(--color-background-soft);
border: 1px solid var(--color-border);
border-radius: 10px;
padding: 1.2rem 1.3rem;
margin: 0;
}
.legal-foot {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
margin-top: 1.25rem;
}
.legal-link {
color: hsla(160, 100%, 37%, 1);
font-weight: 600;
font-size: 0.9rem;
}
.legal-back {
padding: 0.5rem 1.3rem;
border: 1px solid var(--color-border);
border-radius: 8px;
background: var(--color-background);
color: var(--color-text);
cursor: pointer;
font-weight: 600;
}
</style>
+18
View File
@@ -187,6 +187,24 @@ async function clearAppData() {
</svg> </svg>
</RouterLink> </RouterLink>
<RouterLink to="/privacy" class="row row-link">
<div class="row-main">
<span class="row-label">개인정보처리방침</span>
</div>
<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M9 18l6-6-6-6" />
</svg>
</RouterLink>
<RouterLink to="/terms" class="row row-link">
<div class="row-main">
<span class="row-label">이용약관</span>
</div>
<svg class="chevron" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M9 18l6-6-6-6" />
</svg>
</RouterLink>
<div class="row"> <div class="row">
<div class="row-main"> <div class="row-main">
<span class="row-label"> 버전 정보</span> <span class="row-label"> 버전 정보</span>