From 2eaf8695b0d7c466e637eafbcbfdc606514bd382 Mon Sep 17 00:00:00 2001 From: ByungCheol Date: Mon, 29 Jun 2026 09:52:42 +0900 Subject: [PATCH 1/3] =?UTF-8?q?docs(brand):=20=EC=B6=9C=EC=8B=9C=20?= =?UTF-8?q?=ED=9B=84=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EB=B0=B1?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=20=EC=B6=94=EA=B0=80(=EC=97=85=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=8A=B8=EC=95=88=EB=82=B4=C2=B7=EC=8B=A0=EA=B3=A0?= =?UTF-8?q?=EA=B8=80=EA=B4=80=EB=A6=AC=C2=B7=ED=8F=AC=EC=9D=B8=ED=8A=B8?= =?UTF-8?q?=EC=A0=95=EC=B1=85=C2=B7=EB=B6=84=EB=A5=98=EC=88=9C=EC=84=9C?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EB=B3=B4=ED=98=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- brand/LAUNCH-PROGRESS.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/brand/LAUNCH-PROGRESS.md b/brand/LAUNCH-PROGRESS.md index 72bf3a4..90e79cf 100644 --- a/brand/LAUNCH-PROGRESS.md +++ b/brand/LAUNCH-PROGRESS.md @@ -98,6 +98,21 @@ --- +## ๐Ÿ“Œ ์ถœ์‹œ ํ›„ ์—…๋ฐ์ดํŠธ ๋ฐฑ๋กœ๊ทธ (๋‚˜์ค‘์— ์ง„ํ–‰) +> ์ฒซ ์ถœ์‹œ์—” ๋ฏธํฌํ•จ. ์ •์‹ ์ถœ์‹œ ํ›„ ์—…๋ฐ์ดํŠธ๋กœ ์ง„ํ–‰ํ•  ํ•ญ๋ชฉ. + +1. **์•ฑ ์—…๋ฐ์ดํŠธ ์•ˆ๋‚ด ๋ฉ”์‹œ์ง€** โ€” ์ƒˆ ๋ฒ„์ „ ์žˆ์„ ๋•Œ ์•ˆ๋‚ด + ๊ตฌ๊ธ€ํ”Œ๋ ˆ์ด ๋ฐ”๋กœ๊ฐ€๊ธฐ(์Šคํ† ์–ด ํŽ˜์ด์ง€๋กœ ์ด๋™) +2. **๊ด€๋ฆฌ์ž ์‹ ๊ณ ๊ธ€ ๋ชฉ๋ก ํŽ˜์ด์ง€** โ€” ๊ด€๋ฆฌ์ž๊ฐ€ ๊ฒŒ์‹œํŒ ์‹ ๊ณ ๋œ ๊ธ€ ๋ชฉ๋ก์„ ํ™•์ธํ•˜๋Š” ํ™”๋ฉด +3. **๊ฐ€๊ณ„๋ถ€ ์ž‘์„ฑ ํฌ์ธํŠธ** โ€” ๊ฐ€๊ณ„๋ถ€ ๋‚ด์—ญ ์ž‘์„ฑ ์‹œ ๊ฑด๋‹น 10ํฌ์ธํŠธ, **์ผ 50ํฌ์ธํŠธ ์ œํ•œ** +4. **๊ฒŒ์‹œํŒ ํฌ์ธํŠธ ์ •์ฑ… ๊ฐœํŽธ** + 1) ๊ฒŒ์‹œ๊ธ€ ์ž‘์„ฑ 30 โ†’ **50ํฌ์ธํŠธ**, **๋„๋ฐฐ์„ฑ ๊ฒŒ์‹œ๋ฌผ์€ ์ ๋ฆฝ ์ œ์™ธ** + 2) ๊ฒŒ์‹œ๊ธ€ ๋“ฑ๋ก **ํ‚ค์›Œ๋“œ ํ•„ํ„ฐ๋ง** + 3) **์ถ”์ฒœ +5 / ๋น„์ถ” -1** (์ผ์ผ ์ตœ๋Œ€ ์ ๋ฆฝ 50ํฌ์ธํŠธ) + 4) ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž ๊ธ€์— **์ถ”์ฒœ ๋ˆ„๋ฅด๋ฉด +1** (์ผ์ผ 10 ์ œํ•œ), ์ถ”์ฒœ **์ทจ์†Œ ์‹œ -1** +5. **๋ถ„๋ฅ˜ ํ™”๋ฉด ์ˆœ์„œ๋ณ€๊ฒฝ ๋ณดํ˜ธ** โ€” ์Šคํฌ๋กคํ•˜๋‹ค ์‹ค์ˆ˜๋กœ ์ˆœ์„œ ๋ฐ”๋€Œ๋Š” ๊ฒƒ ๋ฐฉ์ง€: ๊ธฐ๋ณธ์€ drag&drop ๋น„ํ™œ์„ฑ, **"์ˆœ์„œ๋ณ€๊ฒฝ" ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ์—๋งŒ** ๋“œ๋ž˜๊ทธ ๊ฐ€๋Šฅ + +--- + ## ๋‹ค์Œ ์„ธ์…˜ ์žฌ๊ฐœ ์ง€์  **์‹ ์›ํ™•์ธ ์Šน์ธ ์—ฌ๋ถ€ ํ™•์ธ** โ†’ ์Šน์ธ๋์œผ๋ฉด "์•ฑ ๋งŒ๋“ค๊ธฐ โ†’ ์ต๋ช… ์…‹์—… โ†’ ํ•„์ˆ˜์„ค์ •/๋“ฑ๋ก์ •๋ณด ์ž…๋ ฅ" ์ง„ํ–‰. ๋Œ€๊ธฐ ์ค‘์ด๋ฉด **ํ…Œ์Šคํ„ฐ 12๋ช… Gmail ํ™•๋ณด** + **์ถœ์‹œ ์ „ ๋‹ค๋“ฌ๊ธฐ(๋นˆ ์ƒํƒœ/์˜คํ”„๋ผ์ธ)** ์ง„ํ–‰ ๊ถŒ์žฅ. From ce7488076aa889b4535104094a62aae04a307dff Mon Sep 17 00:00:00 2001 From: ByungCheol Date: Mon, 29 Jun 2026 17:48:19 +0900 Subject: [PATCH 2/3] =?UTF-8?q?docs(brand):=20=EB=B0=B1=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=97=90=20=EC=95=B1=EC=9E=A0=EA=B8=88-=EC=B9=B4=EB=A9=94?= =?UTF-8?q?=EB=9D=BC=EB=B3=B5=EA=B7=80=20PIN=20=EC=98=A4=EC=9E=91=EB=8F=99?= =?UTF-8?q?=20=EB=B2=84=EA=B7=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- brand/LAUNCH-PROGRESS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/brand/LAUNCH-PROGRESS.md b/brand/LAUNCH-PROGRESS.md index 90e79cf..cbb4e6e 100644 --- a/brand/LAUNCH-PROGRESS.md +++ b/brand/LAUNCH-PROGRESS.md @@ -110,6 +110,7 @@ 3) **์ถ”์ฒœ +5 / ๋น„์ถ” -1** (์ผ์ผ ์ตœ๋Œ€ ์ ๋ฆฝ 50ํฌ์ธํŠธ) 4) ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž ๊ธ€์— **์ถ”์ฒœ ๋ˆ„๋ฅด๋ฉด +1** (์ผ์ผ 10 ์ œํ•œ), ์ถ”์ฒœ **์ทจ์†Œ ์‹œ -1** 5. **๋ถ„๋ฅ˜ ํ™”๋ฉด ์ˆœ์„œ๋ณ€๊ฒฝ ๋ณดํ˜ธ** โ€” ์Šคํฌ๋กคํ•˜๋‹ค ์‹ค์ˆ˜๋กœ ์ˆœ์„œ ๋ฐ”๋€Œ๋Š” ๊ฒƒ ๋ฐฉ์ง€: ๊ธฐ๋ณธ์€ drag&drop ๋น„ํ™œ์„ฑ, **"์ˆœ์„œ๋ณ€๊ฒฝ" ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ์—๋งŒ** ๋“œ๋ž˜๊ทธ ๊ฐ€๋Šฅ +6. **[๋ฒ„๊ทธ] ์•ฑ ์ž ๊ธˆ์ด ์นด๋ฉ”๋ผ/๊ฐค๋Ÿฌ๋ฆฌ ๋ณต๊ท€ ์‹œ ์˜ค์ž‘๋™** โ€” ์˜์ˆ˜์ฆ OCR(์นด๋ฉ”๋ผ)ยท์ด๋ฏธ์ง€ ์„ ํƒ ๋“ฑ ์•ฑ ๋‚ด ๋„ค์ดํ‹ฐ๋ธŒ ๋™์ž‘ ํ›„ ๋Œ์•„์˜ค๋ฉด PIN ์ž ๊ธˆ ํ™”๋ฉด์ด ๋œธ. ์›์ธ: ์•ฑ ์ž ๊ธˆ์ด `visibilitychange`(ํ™”๋ฉด ์ˆจ๊น€)์œผ๋กœ ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋ฅผ ๊ฐ์ง€ํ•˜๋Š”๋ฐ, ์นด๋ฉ”๋ผ/๊ฐค๋Ÿฌ๋ฆฌ ํ˜ธ์ถœ๋„ ์ˆจ๊น€์œผ๋กœ ์ธ์‹๋จ. ์ˆ˜์ •: ๋„ค์ดํ‹ฐ๋ธŒ ๋™์ž‘ ์ง์ „ "๋‹ค์Œ 1ํšŒ ์ž ๊ธˆ ๊ฑด๋„ˆ๋›ฐ๊ธฐ" ํ”Œ๋ž˜๊ทธ๋ฅผ ์„ธ์›Œ ๋ณต๊ท€ ์‹œ ์ž ๊ธˆ ์•ˆ ๊ฑธ๋ฆฌ๊ฒŒ(์ง„์งœ ์•ฑ ์ดํƒˆ๋งŒ ์ž ๊ธˆ). (์ด๋ฒˆ ์„ธ์…˜ ์•ฑ์ž ๊ธˆ ์ถ”๊ฐ€๋กœ ์ƒ๊ธด ํšŒ๊ท€) --- From a0aba1d5788fde3bc46c62bbb4873ee2fec4f850 Mon Sep 17 00:00:00 2001 From: ByungCheol Date: Mon, 29 Jun 2026 20:25:50 +0900 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20=EC=95=B1=20=EC=9E=A0=EA=B8=88?= =?UTF-8?q?=EC=9D=B4=20=EC=B9=B4=EB=A9=94=EB=9D=BC/=EA=B0=A4=EB=9F=AC?= =?UTF-8?q?=EB=A6=AC/=EA=B3=B5=EC=9C=A0=20=EB=B3=B5=EA=B7=80=20=EC=8B=9C?= =?UTF-8?q?=20=EC=98=A4=EC=9E=91=EB=8F=99=ED=95=98=EB=8D=98=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ์›์ธ: visibilitychange(์ˆจ๊น€)๋กœ ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋ฅผ ๊ฐ์ง€ํ•˜๋Š”๋ฐ ์นด๋ฉ”๋ผ/ํŒŒ์ผ์„ ํƒ/๊ณต์œ  ํ˜ธ์ถœ ์‹œ์—๋„ ์›น๋ทฐ๊ฐ€ ์ˆจ๊ฒจ์ ธ ์ž ๊ธˆ์ด ๊ฑธ๋ ธ์Œ(๋ณต๊ท€ ์‹œ PIN ํ™”๋ฉด) - appLock.suppressLock(): ๋„ค์ดํ‹ฐ๋ธŒ ๋™์ž‘ ์ง์ „ ์ผ์‹œ์ค‘์ง€ ์ฐฝ์„ ์—ด์–ด ๊ทธ ๋ณต๊ท€๋Š” ์ž ๊ทธ์ง€ ์•Š์Œ - ํŒŒ์ผ์„ ํƒ(input[type=file])์€ App.vue ์ „์—ญ ํด๋ฆญ ๋ฆฌ์Šค๋„ˆ๋กœ ์ผ๊ด„ ์ฒ˜๋ฆฌ - ์นด๋ฉ”๋ผ(OCR)ยท๊ณต์œ (๋ฐฑ์—…)๋Š” ํ˜ธ์ถœ๋ถ€์—์„œ ์ง์ ‘ suppressLock ํ˜ธ์ถœ - ๋ณต๊ท€(visible) ์‹œ ์ผ์‹œ์ค‘์ง€ ์ฐฝ ํ•ด์ œ Co-Authored-By: Claude Opus 4.8 --- src/App.vue | 9 +++++++++ src/composables/appLock.js | 15 ++++++++++++++- src/utils/backup.js | 2 ++ src/views/account/AccountView.vue | 2 ++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/App.vue b/src/App.vue index 2890ba5..d11e213 100644 --- a/src/App.vue +++ b/src/App.vue @@ -76,8 +76,15 @@ function retryReload() { // ===== ์•ฑ ์ž ๊ธˆ(PIN) ===== // ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋กœ ๊ฐ€๋ฉด ์ž ๊ฐ€์„œ, ๋ณต๊ท€ ์‹œ ์ž ๊ธˆ ํ™”๋ฉด์ด ๋–  ์žˆ๋„๋ก ํ•œ๋‹ค. +// ๋‹จ, ์นด๋ฉ”๋ผ/๊ฐค๋Ÿฌ๋ฆฌ/๊ณต์œ  ๋“ฑ ์•ฑ ๋‚ด ๋„ค์ดํ‹ฐ๋ธŒ ๋™์ž‘์œผ๋กœ ์ธํ•œ ์ผ์‹œ ์ˆจ๊น€์€ ์ œ์™ธ(suppress). function onVisibility() { if (document.hidden) appLock.lock() + else appLock.clearSuppress() // ๋ณต๊ท€ โ€” ์ผ์‹œ์ค‘์ง€ ์ฐฝ ํ•ด์ œ +} +// ํŒŒ์ผ ์„ ํƒ(input[type=file]) ํด๋ฆญ ์‹œ ๋‹ค์Œ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ „ํ™˜์„ ์ž ๊ธˆ์—์„œ ์ œ์™ธ +function onFileInputClick(e) { + const t = e.target + if (t && t.tagName === 'INPUT' && t.type === 'file') appLock.suppressLock() } // ===== ํ† ์ŠคํŠธ (๋’ค๋กœ๊ฐ€๊ธฐ ์ข…๋ฃŒ ์•ˆ๋‚ด ๋“ฑ) ===== @@ -100,6 +107,7 @@ onMounted(() => { // ์•ฑ ์ž ๊ธˆ: ์ €์žฅ๋œ ์„ธ์…˜์œผ๋กœ ์žฌ์ง„์ž…ํ•˜๋Š” ๊ฒฝ์šฐ ์‹œ์ž‘ ์‹œ ์ž ๊ธˆ(์ƒˆ ๋กœ๊ทธ์ธ ์งํ›„๋Š” ์ž ๊ทธ์ง€ ์•Š์Œ) if (appLock.state.enabled && appLock.hasSession()) appLock.lock() document.addEventListener('visibilitychange', onVisibility) + document.addEventListener('click', onFileInputClick, true) ui.loadSignupEnabled() // ํšŒ์›๊ฐ€์ž… ํ—ˆ์šฉ ์—ฌ๋ถ€ ์„ ๋กœ๋”ฉ(๋žœ๋”ฉ/๋กœ๊ทธ์ธ ๊ฐ€์ž… ๋ฒ„ํŠผ์— ๋ฐ˜์˜) // ๋กœ๊ทธ์ธ ์ƒํƒœ๋ฉด ์ „์ฒด ํ”„๋กœํ•„(์•„๋ฐ”ํƒ€ยทํฌ์ธํŠธ) ์ตœ์‹ ํ™” โ€” ์žฌ๋กœ๊ทธ์ธ ์—†์ด ํ—ค๋” ์•„๋ฐ”ํƒ€ ๋ฐ˜์˜ if (auth.isAuthenticated) { @@ -123,6 +131,7 @@ onUnmounted(() => { window.removeEventListener('net:error', onNetError) window.removeEventListener('app:toast', onToast) document.removeEventListener('visibilitychange', onVisibility) + document.removeEventListener('click', onFileInputClick, true) clearTimeout(netTimer) clearTimeout(toastTimer) }) diff --git a/src/composables/appLock.js b/src/composables/appLock.js index 309780c..f33422d 100644 --- a/src/composables/appLock.js +++ b/src/composables/appLock.js @@ -13,6 +13,10 @@ const state = reactive({ locked: false, }) +// ์นด๋ฉ”๋ผ/๊ฐค๋Ÿฌ๋ฆฌ/๊ณต์œ  ๋“ฑ ์•ฑ ๋‚ด ๋„ค์ดํ‹ฐ๋ธŒ ๋™์ž‘์„ ํ˜ธ์ถœํ•˜๋ฉด ์›น๋ทฐ๊ฐ€ ์ž ๊น ๋ฐฑ๊ทธ๋ผ์šด๋“œ๋กœ +// ์ธ์‹๋ผ ์ž ๊ธˆ์ด ๊ฑธ๋ฆฌ๋Š” ์˜ค์ž‘๋™์„ ๋ง‰๊ธฐ ์œ„ํ•œ "์ž ๊ธˆ ์ผ์‹œ์ค‘์ง€" ์ฐฝ(timestamp). +let suppressUntil = 0 + async function hashPin(pin) { const data = new TextEncoder().encode('dondwaeji-applock:' + pin) const buf = await crypto.subtle.digest('SHA-256', data) @@ -39,10 +43,19 @@ export const appLock = { state.enabled = false state.locked = false }, - /** ์ž ๊ธˆ ๊ฑธ๊ธฐ(ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ์—๋งŒ) */ + /** ์ž ๊ธˆ ๊ฑธ๊ธฐ(ํ™œ์„ฑํ™” + ์ผ์‹œ์ค‘์ง€ ์ฐฝ ๋ฐ–์ผ ๋•Œ๋งŒ) */ lock() { + if (Date.now() < suppressUntil) return // ๋„ค์ดํ‹ฐ๋ธŒ ๋™์ž‘ ๋ณต๊ท€ โ€” ์ž ๊ทธ์ง€ ์•Š์Œ if (state.enabled) state.locked = true }, + /** ๋‹ค์Œ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ „ํ™˜์„ ์ž ๊ทธ์ง€ ์•Š์Œ(์นด๋ฉ”๋ผ/๊ฐค๋Ÿฌ๋ฆฌ/๊ณต์œ  ํ˜ธ์ถœ ์ง์ „์— ์‚ฌ์šฉ) */ + suppressLock(ms = 60000) { + suppressUntil = Date.now() + ms + }, + /** ์ผ์‹œ์ค‘์ง€ ์ฐฝ ํ•ด์ œ(์•ฑ์œผ๋กœ ๋ณต๊ท€ ์‹œ) */ + clearSuppress() { + suppressUntil = 0 + }, /** ์ž ๊ธˆ ํ’€๊ธฐ(์ด๋ฒˆ ์„ธ์…˜) */ unlock() { state.locked = false diff --git a/src/utils/backup.js b/src/utils/backup.js index dafe109..e70cad1 100644 --- a/src/utils/backup.js +++ b/src/utils/backup.js @@ -2,6 +2,7 @@ // ์ฐธ์กฐ๋Š” ์ด๋ฆ„ ๊ธฐ์ค€(๊ณ„์ขŒ๋ช…ยท๋ถ„๋ฅ˜๋ช…ยทํƒœ๊ทธ๋ช…)์œผ๋กœ ์ €์žฅํ•ด ์ถ”ํ›„ '๊ฐ€์ ธ์˜ค๊ธฐ(๋ณต๊ตฌ)' ์‹œ ์žฌ๋งคํ•‘์ด ๋‹จ์ˆœํ•˜๋„๋ก ํ•œ๋‹ค. import { Capacitor } from '@capacitor/core' import { accountApi } from '@/api/accountApi' +import { appLock } from '@/composables/appLock' async function safe(promise) { try { @@ -25,6 +26,7 @@ async function saveWorkbook(XLSX, wb, name) { const { Share } = await import('@capacitor/share') const res = await Filesystem.writeFile({ path: name, data: base64, directory: Directory.Cache }) try { + appLock.suppressLock() // ๊ณต์œ  ์‹œํŠธ ๋ณต๊ท€ ์‹œ ์•ฑ ์ž ๊ธˆ ์˜ค์ž‘๋™ ๋ฐฉ์ง€ await Share.share({ title: name, text: '๊ฐ€๊ณ„๋ถ€ ๋ฐฑ์—… ํŒŒ์ผ', url: res.uri }) } catch { /* ๊ณต์œ  ์ทจ์†Œ โ€” ํŒŒ์ผ์€ ์บ์‹œ์— ์ €์žฅ๋จ */ diff --git a/src/views/account/AccountView.vue b/src/views/account/AccountView.vue index e6eb333..85bd044 100644 --- a/src/views/account/AccountView.vue +++ b/src/views/account/AccountView.vue @@ -4,6 +4,7 @@ import { accountApi } from '@/api/accountApi' import { reminders } from '@/native/reminders' import { useAuthStore } from '@/stores/auth' import { useDialog } from '@/composables/dialog' +import { appLock } from '@/composables/appLock' import IconBtn from '@/components/ui/IconBtn.vue' import { imageToBlob, parseReceiptText } from '@/utils/receiptOcr' import { cardNotif } from '@/native/cardNotif' @@ -136,6 +137,7 @@ async function chooseGallery() { // ๋„ค์ดํ‹ฐ๋ธŒ ์ „์šฉ โ€” ์นด๋ฉ”๋ผ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ด ์‹œ์ ์—๋งŒ ๋™์  ๋กœ๋“œ async function captureFrom(sourceKey) { try { + appLock.suppressLock() // ์นด๋ฉ”๋ผ ๋ณต๊ท€ ์‹œ ์•ฑ ์ž ๊ธˆ ์˜ค์ž‘๋™ ๋ฐฉ์ง€ const { Camera, CameraResultType, CameraSource } = await import('@capacitor/camera') const photo = await Camera.getPhoto({ source: sourceKey === 'CAMERA' ? CameraSource.Camera : CameraSource.Photos,