chore(i18n): 전체 화면 '고정지출' → '정기결제' 문구 변경
Deploy / deploy (push) Failing after 11m5s

- 사이드바·헤더·홈·업그레이드·데모·정기결제/내역 화면 라벨 통일
- 백업 엑셀 시트명(backup.js)은 기존 백업 복원 호환 위해 '고정지출' 유지

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
sb
2026-07-07 22:20:24 +09:00
parent 98ad7b9e98
commit 1ee264f2ab
9 changed files with 26 additions and 26 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ const TITLES = {
'demo-locked': '둘러보기', 'demo-locked': '둘러보기',
'account-entries': '가계부 내역', 'account-entries': '가계부 내역',
'account-stats': '통계', 'account-stats': '통계',
'account-recurrings': '고정 지출', 'account-recurrings': '정기결제',
'account-wallets': '계좌 관리', 'account-wallets': '계좌 관리',
'account-categories': '분류 관리', 'account-categories': '분류 관리',
'account-budget': '예산 설정', 'account-budget': '예산 설정',
+1 -1
View File
@@ -52,7 +52,7 @@ const icons = {
</RouterLink> </RouterLink>
<RouterLink to="/account/recurrings" class="menu-item"> <RouterLink to="/account/recurrings" class="menu-item">
<svg class="menu-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" v-html="icons.recurrings" /> <svg class="menu-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" v-html="icons.recurrings" />
<span>고정 지출</span> <span>정기결제</span>
<!-- 무료도 2건까지 사용 가능 🔒 미표시 --> <!-- 무료도 2건까지 사용 가능 🔒 미표시 -->
</RouterLink> </RouterLink>
<RouterLink to="/account/wallets" class="menu-item"> <RouterLink to="/account/wallets" class="menu-item">
+1 -1
View File
@@ -161,7 +161,7 @@ const router = createRouter({
path: '/account/recurrings', path: '/account/recurrings',
name: 'account-recurrings', name: 'account-recurrings',
component: () => import('../views/account/RecurringView.vue'), component: () => import('../views/account/RecurringView.vue'),
// 고정지출은 무료도 2건까지 체험(초과 시 업셀) — 유료 게이트 해제 // 정기결제은 무료도 2건까지 체험(초과 시 업셀) — 유료 게이트 해제
meta: { requiresAuth: true }, meta: { requiresAuth: true },
}, },
// 설정 (앱 하단 내비게이션 → 설정) // 설정 (앱 하단 내비게이션 → 설정)
+1 -1
View File
@@ -26,7 +26,7 @@ function goLogin() {
<div class="locked"> <div class="locked">
<div class="lock-big">🔒</div> <div class="lock-big">🔒</div>
<h2>{{ label }} 로그인 이용할 있어요</h2> <h2>{{ label }} 로그인 이용할 있어요</h2>
<p class="desc">둘러보기에서는 가계부 내역 · 고정 지출 · 계좌 관리 · 분류 관리만 미리 있습니다.<br />로그인하면 통계 · 예산 · 태그 · 게시판까지 모두 사용할 있어요.</p> <p class="desc">둘러보기에서는 가계부 내역 · 정기결제 · 계좌 관리 · 분류 관리만 미리 있습니다.<br />로그인하면 통계 · 예산 · 태그 · 게시판까지 모두 사용할 있어요.</p>
<button type="button" class="cta-btn" @click="goLogin">로그인 / 시작하기</button> <button type="button" class="cta-btn" @click="goLogin">로그인 / 시작하기</button>
</div> </div>
</template> </template>
+1 -1
View File
@@ -133,7 +133,7 @@ const shortcuts = [
{ to: '/account/stats', icon: '📊', label: '통계', desc: '차트로 보기' }, { to: '/account/stats', icon: '📊', label: '통계', desc: '차트로 보기' },
{ to: '/account/wallets', icon: '🏦', label: '계좌 관리', desc: '자산·부채' }, { to: '/account/wallets', icon: '🏦', label: '계좌 관리', desc: '자산·부채' },
{ to: '/account/budget', icon: '🎯', label: '예산 설정', desc: '예산 대비 지출' }, { to: '/account/budget', icon: '🎯', label: '예산 설정', desc: '예산 대비 지출' },
{ to: '/account/recurrings', icon: '🔁', label: '고정 지출', desc: '매월·매년 반복' }, { to: '/account/recurrings', icon: '🔁', label: '정기결제', desc: '매월·매년 반복' },
{ to: '/account/categories', icon: '🗂️', label: '분류 관리', desc: '카테고리' }, { to: '/account/categories', icon: '🗂️', label: '분류 관리', desc: '카테고리' },
] ]
+2 -2
View File
@@ -59,7 +59,7 @@ async function buy(product) {
const freeFeatures = [ const freeFeatures = [
'가계부 내역 기록 · 조회', '가계부 내역 기록 · 조회',
'계좌 관리 (종류별 2개)', '계좌 관리 (종류별 2개)',
'고정 지출 (2개까지 자동 반영)', '정기결제 (2개까지 자동 반영)',
'자주 쓰는 내역', '자주 쓰는 내역',
'월별 수입/지출 요약', '월별 수입/지출 요약',
'기본 분류', '기본 분류',
@@ -68,7 +68,7 @@ const freeFeatures = [
const premiumFeatures = [ const premiumFeatures = [
'전체 통계 (분류별·추이·자산)', '전체 통계 (분류별·추이·자산)',
'예산 설정', '예산 설정',
'계좌 · 고정 지출 무제한', '계좌 · 정기결제 무제한',
'영수증 OCR 인식', '영수증 OCR 인식',
'카드 알림 자동 인식', '카드 알림 자동 인식',
'투자 · 자산 관리', '투자 · 자산 관리',
+1 -1
View File
@@ -118,7 +118,7 @@ onBeforeUnmount(() => sortable?.destroy())
<p v-if="reorderMode" class="hint sm"> 손잡이를 끌어 순서를 바꾸세요. 끝나면 <b>완료</b>.</p> <p v-if="reorderMode" class="hint sm"> 손잡이를 끌어 순서를 바꾸세요. 끝나면 <b>완료</b>.</p>
<form class="new-tag" @submit.prevent="addTag"> <form class="new-tag" @submit.prevent="addTag">
<input v-model="newName" type="text" placeholder="새 태그 이름 (예: 식비, 고정지출)" /> <input v-model="newName" type="text" placeholder="새 태그 이름 (예: 식비, 정기결제)" />
<IconBtn icon="plus" title="추가" variant="primary" type="submit" /> <IconBtn icon="plus" title="추가" variant="primary" type="submit" />
</form> </form>
+11 -11
View File
@@ -712,7 +712,7 @@ async function remove(e) {
} }
} }
// 고정지출 등록 확인 모달 상태 (네이티브 confirm 대신 — PC 팝업 제목 'sb_pt' 노출 방지) // 정기결제 등록 확인 모달 상태 (네이티브 confirm 대신 — PC 팝업 제목 'sb_pt' 노출 방지)
const recurConfirm = reactive({ open: false, title: '', dom: 1, amount: 0, payload: null }) const recurConfirm = reactive({ open: false, title: '', dom: 1, amount: 0, payload: null })
const flashMsg = ref('') const flashMsg = ref('')
let flashTimer = null let flashTimer = null
@@ -722,9 +722,9 @@ function flash(msg) {
flashTimer = setTimeout(() => (flashMsg.value = ''), 2800) flashTimer = setTimeout(() => (flashMsg.value = ''), 2800)
} }
// 현재 수정 중인 내역을 '매월' 고정지출로 등록 — 확인 모달을 띄운다 // 현재 수정 중인 내역을 '매월' 정기결제로 등록 — 확인 모달을 띄운다
function registerAsRecurring() { function registerAsRecurring() {
if (form.type === 'REPAYMENT') return // 고정지출은 수입/지출/이체만 if (form.type === 'REPAYMENT') return // 정기결제은 수입/지출/이체만
const amount = Number(form.amount) const amount = Number(form.amount)
if (!amount || amount <= 0) { if (!amount || amount <= 0) {
formError.value = '금액을 올바르게 입력하세요.' formError.value = '금액을 올바르게 입력하세요.'
@@ -733,7 +733,7 @@ function registerAsRecurring() {
const title = const title =
(form.memo || '').trim() || (form.memo || '').trim() ||
(form.category || '').trim() || (form.category || '').trim() ||
(form.type === 'INCOME' ? '정기 수입' : form.type === 'TRANSFER' ? '정기 이체' : '고정 지출') (form.type === 'INCOME' ? '정기 수입' : form.type === 'TRANSFER' ? '정기 이체' : '정기결제')
const base = form.entryDate || todayStr() const base = form.entryDate || todayStr()
const [y, m, d] = base.split('-').map(Number) const [y, m, d] = base.split('-').map(Number)
const dom = d const dom = d
@@ -766,9 +766,9 @@ async function doRegisterRecurring() {
await accountApi.createRecurring(recurConfirm.payload) await accountApi.createRecurring(recurConfirm.payload)
recurConfirm.open = false recurConfirm.open = false
formOpen.value = false formOpen.value = false
flash('고정지출로 등록했습니다. 고정지출 화면에서 주기를 변경할 수 있어요.') flash('정기결제로 등록했습니다. 정기결제 화면에서 주기를 변경할 수 있어요.')
} catch (e) { } catch (e) {
formError.value = e.response?.data?.message || '고정지출 등록에 실패했습니다.' formError.value = e.response?.data?.message || '정기결제 등록에 실패했습니다.'
recurConfirm.open = false recurConfirm.open = false
} finally { } finally {
submitting.value = false submitting.value = false
@@ -941,7 +941,7 @@ async function analyzePasted() {
onMounted(async () => { onMounted(async () => {
// 진입 시 밀린 정기 거래를 먼저 반영한 뒤 목록을 불러온다 (중복 없이) // 진입 시 밀린 정기 거래를 먼저 반영한 뒤 목록을 불러온다 (중복 없이)
// 고정지출은 무료도 2건까지 사용 가능 → 모든 로그인 사용자에 대해 실행 // 정기결제은 무료도 2건까지 사용 가능 → 모든 로그인 사용자에 대해 실행
try { try {
await accountApi.runRecurrings() await accountApi.runRecurrings()
} catch { } catch {
@@ -1302,7 +1302,7 @@ onMounted(async () => {
class="to-recurring" class="to-recurring"
:disabled="submitting" :disabled="submitting"
@click="registerAsRecurring" @click="registerAsRecurring"
>🔁 내역을 고정지출 등록</button> >🔁 내역을 정기결제 등록</button>
<button <button
v-if="editId && (form.type === 'INCOME' || form.type === 'EXPENSE')" v-if="editId && (form.type === 'INCOME' || form.type === 'EXPENSE')"
@@ -1346,17 +1346,17 @@ onMounted(async () => {
</Transition> </Transition>
</Teleport> </Teleport>
<!-- 고정지출 등록 확인 (네이티브 confirm 대체 제목 'sb_pt' 노출 방지) --> <!-- 정기결제 등록 확인 (네이티브 confirm 대체 제목 'sb_pt' 노출 방지) -->
<Teleport to="body"> <Teleport to="body">
<Transition name="fade"> <Transition name="fade">
<div v-if="recurConfirm.open" class="modal-backdrop" @click.self="recurConfirm.open = false"> <div v-if="recurConfirm.open" class="modal-backdrop" @click.self="recurConfirm.open = false">
<div class="modal confirm-modal" role="dialog" aria-modal="true"> <div class="modal confirm-modal" role="dialog" aria-modal="true">
<h2>고정지출 등록</h2> <h2>정기결제 등록</h2>
<p class="confirm-body"> <p class="confirm-body">
<b>{{ recurConfirm.title }}</b><br /> <b>{{ recurConfirm.title }}</b><br />
매월 <b>{{ recurConfirm.dom }}</b> · <b>{{ won(recurConfirm.amount) }}</b> 으로 등록할까요? 매월 <b>{{ recurConfirm.dom }}</b> · <b>{{ won(recurConfirm.amount) }}</b> 으로 등록할까요?
</p> </p>
<p class="confirm-sub">주기·시작일은 고정지출 화면에서 변경할 있어요.</p> <p class="confirm-sub">주기·시작일은 정기결제 화면에서 변경할 있어요.</p>
<div class="buttons"> <div class="buttons">
<IconBtn icon="close" title="취소" @click="recurConfirm.open = false" /> <IconBtn icon="close" title="취소" @click="recurConfirm.open = false" />
<IconBtn icon="save" title="등록" variant="primary" :disabled="submitting" @click="doRegisterRecurring" /> <IconBtn icon="save" title="등록" variant="primary" :disabled="submitting" @click="doRegisterRecurring" />
+7 -7
View File
@@ -16,7 +16,7 @@ const router = useRouter()
const recurrings = ref([]) const recurrings = ref([])
const wallets = ref([]) const wallets = ref([])
// 무료 회원 고정지출 등록 한도(써보고 → 업셀). 유료는 무제한. 백엔드에서도 강제. // 무료 회원 정기결제 등록 한도(써보고 → 업셀). 유료는 무제한. 백엔드에서도 강제.
const FREE_RECURRING_LIMIT = 2 const FREE_RECURRING_LIMIT = 2
const atRecurringLimit = computed( const atRecurringLimit = computed(
() => !auth.isPremium && recurrings.value.length >= FREE_RECURRING_LIMIT, () => !auth.isPremium && recurrings.value.length >= FREE_RECURRING_LIMIT,
@@ -158,7 +158,7 @@ async function load() {
async function runNow() { async function runNow() {
try { try {
const res = await accountApi.runRecurrings() const res = await accountApi.runRecurrings()
alert(`${res.generated}건의 고정 지출가 반영되었습니다.`) alert(`${res.generated}건의 정기결제가 반영되었습니다.`)
await load() await load()
} catch (e) { } catch (e) {
alert(e.response?.data?.message || '반영에 실패했습니다.') alert(e.response?.data?.message || '반영에 실패했습니다.')
@@ -168,7 +168,7 @@ async function runNow() {
async function openCreate() { async function openCreate() {
if (atRecurringLimit.value) { if (atRecurringLimit.value) {
const go = await dialog.confirm( const go = await dialog.confirm(
`무료 회원은 고정 지출을 ${FREE_RECURRING_LIMIT}개까지 등록할 수 있어요.\n유료로 업그레이드하면 무제한이에요.`, `무료 회원은 정기결제를 ${FREE_RECURRING_LIMIT}개까지 등록할 수 있어요.\n유료로 업그레이드하면 무제한이에요.`,
{ title: '등록 한도', okText: '업그레이드', cancelText: '닫기' }, { title: '등록 한도', okText: '업그레이드', cancelText: '닫기' },
) )
if (go) router.push('/upgrade') if (go) router.push('/upgrade')
@@ -237,7 +237,7 @@ async function submit() {
} }
async function remove(r) { async function remove(r) {
if (!(await dialog.confirm(`'${r.title}' 고정 지출를 삭제할까요?\n이미 생성된 내역은 유지됩니다.`, { title: '고정지출 삭제', danger: true }))) return if (!(await dialog.confirm(`'${r.title}' 정기결제를 삭제할까요?\n이미 생성된 내역은 유지됩니다.`, { title: '정기결제 삭제', danger: true }))) return
try { try {
await accountApi.removeRecurring(r.id) await accountApi.removeRecurring(r.id)
await load() await load()
@@ -254,7 +254,7 @@ onMounted(load)
<p class="hint">등록한 주기에 맞춰 가계부 진입 자동으로 <b>'확인 필요'</b> 내역이 생성됩니다(중복 없이). 가계부에서 실제 결제·이체를 확인한 <b>'확인'</b> 눌러 확정하세요.</p> <p class="hint">등록한 주기에 맞춰 가계부 진입 자동으로 <b>'확인 필요'</b> 내역이 생성됩니다(중복 없이). 가계부에서 실제 결제·이체를 확인한 <b>'확인'</b> 눌러 확정하세요.</p>
<div class="recur-actions"> <div class="recur-actions">
<IconBtn icon="refresh" title="지금 반영" @click="runNow" /> <IconBtn icon="refresh" title="지금 반영" @click="runNow" />
<IconBtn icon="plus" title="고정 지출 추가" variant="primary" @click="openCreate" /> <IconBtn icon="plus" title="정기결제 추가" variant="primary" @click="openCreate" />
</div> </div>
<p v-if="atRecurringLimit" class="msg recur-limit"> <p v-if="atRecurringLimit" class="msg recur-limit">
무료 {{ FREE_RECURRING_LIMIT }}개까지 등록 완료 · 무료 {{ FREE_RECURRING_LIMIT }}개까지 등록 완료 ·
@@ -295,10 +295,10 @@ onMounted(load)
</div> </div>
</section> </section>
</div> </div>
<p v-else-if="!loading" class="msg">등록된 고정 지출 없습니다.</p> <p v-else-if="!loading" class="msg">등록된 정기결제 없습니다.</p>
<!-- 추가/수정 모달 --> <!-- 추가/수정 모달 -->
<AppModal v-model="formOpen" :title="`고정 지출 ${editId ? '수정' : '추가'}`"> <AppModal v-model="formOpen" :title="`정기결제 ${editId ? '수정' : '추가'}`">
<form class="recur-form" @submit.prevent="submit"> <form class="recur-form" @submit.prevent="submit">
<label>이름<input v-model="form.title" type="text" placeholder="예: 월세, 급여" :disabled="submitting" /></label> <label>이름<input v-model="form.title" type="text" placeholder="예: 월세, 급여" :disabled="submitting" /></label>