๊ณ ๊ธ ํด๋ฆฝ๋ณด๋ ๋งค๋์ - PyQt6 ๊ธฐ๋ฐ์ ํ๋์ ์ด๊ณ ๊ฐ๋ ฅํ ํด๋ฆฝ๋ณด๋ ๊ด๋ฆฌ ๋๊ตฌ
- ํ ์คํธ, ์ด๋ฏธ์ง, ๋งํฌ, ์ฝ๋, ์์, ํ์ผ/ํด๋ ์๋ ๋ถ๋ฅ ๋ฐ ์ ์ฅ
- ์ต๋ 500๊ฐ ํญ๋ชฉ ์ ์ฅ (์ค์ ๊ฐ๋ฅ)
- ๐ ์ค์ ํญ๋ชฉ ๊ณ ์ ๋ฐ ๋๋๊ทธ ์ ๋ ฌ ๊ธฐ๋ฅ
- ๐ท๏ธ ํ๊ทธ ์์คํ ์ผ๋ก ํญ๋ชฉ ์ ๋ฆฌ
- โญ ๋ถ๋งํฌ ๊ธฐ๋ฅ์ผ๋ก ์ฆ๊ฒจ์ฐพ๊ธฐ ๊ด๋ฆฌ
- ๐ ์ปฌ๋ ์ ๊ธฐ๋ฅ์ผ๋ก ํญ๋ชฉ ๊ทธ๋ฃนํ
- ๐ ํญ๋ชฉ๋ณ ๋ฉ๋ชจ ์ฒจ๋ถ ๊ธฐ๋ฅ
- ํ์ผ ๋ณต์ฌ๋ ๋ค์ค ์ ํ์ ํ๋์
FILEํญ๋ชฉ์ผ๋ก ์ ์ฅํ๊ณ , paste-last/๋ฏธ๋ ์ฐฝ/์ ํ ๋ถ์ฌ๋ฃ๊ธฐ์์ ๋ก์ปฌ file URL ํด๋ฆฝ๋ณด๋๋ฅผ ๋ณต์
- PBKDF2-HMAC-SHA256 + Fernet ๊ธฐ๋ฐ ์ํธํ๋ก ๋ฏผ๊ฐํ ๋ฐ์ดํฐ ์์ ๋ณด๊ด
- ๋ง์คํฐ ๋น๋ฐ๋ฒํธ ๊ธฐ๋ฐ ์ ๊ธ
- v10.2: ๋น๋ฐ๋ฒํธ ๊ฐ๋ ๊ฒ์ฆ (8์ ์ด์, ์ซ์+ํน์๋ฌธ์)
- 5๋ถ ์๋ ์ ๊ธ ํ์ด๋จธ
- ๋ง์คํฐ ๋น๋ฐ๋ฒํธ ๋ณ๊ฒฝ ์ ๊ธฐ์กด ๋ณด๊ด ํญ๋ชฉ ์ ์ฒด ์ฌ์ํธํ
- ๋ณตํธํ ํ ํด๋ฆฝ๋ณด๋๋ก ๋ณต์ฌํ ํ ์คํธ๋ 30์ด ๋ค ์๋ ์ญ์
- ์ค์ ์์ ์ ์ ๊ธ ํ๋ฉด์์
Reset ๋ณด๊ดํจ์ผ๋ก ๋ณต๊ตฌ ๊ฐ๋ฅ
- ํจํด ๋งค์นญ ๊ธฐ๋ฐ ์๋ ์ฒ๋ฆฌ
- v10.2: URL ์ ๋ชฉ ๊ฐ์ ธ์ค๊ธฐ ๊ฐ์ (ํ์์์/์๋ฌ ์ฒ๋ฆฌ ๊ฐํ)
fetch_title์ ์ฒซ URL๋ง ์ถ์ถํ๋ฉฐ ๋ก์ปฌ/์ฌ์ค/๋ฉํ๋ฐ์ดํฐ ์ฃผ์๋ ๋ณด์์ ์ฐจ๋จ- ์ ํ๋ฒํธ ์๋ ํฌ๋งทํ
(
02, ์ผ๋ฐ ์ง์ญ๋ฒํธ,0505,1588/1661/1800๋ฅ ๋ํ๋ฒํธ ํฌํจ) - ์ด๋ฉ์ผ ์ ๊ทํ
- ํ ์คํธ ๋ณํ (๋์๋ฌธ์, ํธ๋ฆผ ๋ฑ)
- ๋๊ธฐ ํ
์คํธ ์ก์
(
format_phone/format_email/transform)์ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ history row์ clipboard์ ๋ค์ ๋ฐ์ํ๊ณ , ๊ฐ์ ๋ฐฐ์น์ ํ์ ์ก์ ์ ๋ณํ๋ ํ ์คํธ ๊ธฐ์ค์ผ๋ก ํ๊ฐ - v10.2: ์ ๊ท์ ํจํด ์ ํจ์ฑ ๊ฒ์ฆ
- ์ญ์ ํญ๋ชฉ 7์ผ๊ฐ ๋ณด๊ด ํ ์๋ ์๊ตฌ ์ญ์
- v10.2: ํด์งํต ๋ค์ด์ผ๋ก๊ทธ ๋ค์ค ์ ํ ์ง์
- ์ํด๋ฆญ ๋ณต์ ๋ฐ ์๊ตฌ ์ญ์
- ์คํ ์ทจ์ ๊ฐ๋ฅํ ์์ ํ ์ญ์
- ์ ์ฒด ๊ธฐ๋ก ์ญ์ ๋ ๊ณ ์ ํญ๋ชฉ์ ์ ์ธํ๊ณ ํด์งํต ์ด๋์ผ๋ก ์ฒ๋ฆฌ
- ์์ฃผ ์ฌ์ฉํ๋ ํ ์คํธ ํ ํ๋ฆฟ ์ ์ฅ
- v10.2: ์ค๋ํซ ์์ ๊ธฐ๋ฅ ์ถ๊ฐ
- ์นดํ ๊ณ ๋ฆฌ๋ณ ์ ๋ฆฌ
- ๋๋ธํด๋ฆญ/๋ฒํผ์ผ๋ก ์ฆ์ ๋ณต์ฌ
- ์ฑ ๋ด๋ถ ์ ์ฉ ๋จ์ถํค(
shortcut) ๋ฑ๋ก ๋ฐ ์คํ ์ง์ - ๊ธฐ๋ณธ ๋จ์ถํค/๊ธ๋ก๋ฒ ํซํค/๋ค๋ฅธ ์ค๋ํซ๊ณผ์ ์ถฉ๋ ๊ฒ์ฆ
- JSON, CSV, Markdown ๋ด๋ณด๋ด๊ธฐ / JSON, CSV ๊ฐ์ ธ์ค๊ธฐ ์ง์
- ๋ ์ง ๋ฐ ํ์ ํํฐ๋ง
- JSON์
IMAGEํญ๋ชฉ์image_data_b64๋ก ๋ณด์กดํ๋ฉฐ, CSV/Markdown์ ์ด๋ฏธ์ง ํ๋ ์ด์คํ๋๋ง ๊ธฐ๋ก FILEํญ๋ชฉ์ ๋ฐ์ด๋๋ฆฌ ๋์ ๊ฒฝ๋ก ๋ชฉ๋ก๋ง ๋ด๋ณด๋ด๋ฉฐ, JSON์file_paths/file_path, CSV/Markdown์ newline ๊ฒฝ๋ก ๋ชฉ๋ก์ ์ฌ์ฉ- JSON ๋ง์ด๊ทธ๋ ์ด์ ๋ชจ๋ (ํ์คํ ๋ฆฌ ํญ๋ชฉ์ ํ๊ทธ/๋ฉ๋ชจ/๋ถ๋งํฌ + ์ปฌ๋ ์ ์ ์/ID ๋งคํ ์ ๋ณด ํฌํจ, ์ค๋ํซ/๊ท์น/ํซํค/๋ณด์ ๋ณด๊ดํจ ์ ์ธ)
- JSON import๋ ISO-8601/tz timestamp๋ฅผ ์๋ณธ ์๊ฐ ๊ธฐ์ค ์ฑ ํ์ค ์๊ฐ ๋ฌธ์์ด๋ก ์ ๊ทํํ๊ณ , ์์ ๋ถ๋ timestamp๋ import ์๊ฐ์ผ๋ก ๋์ฒด
- CSV import๋ ์ด๋ฏธ์ง ํ๋ ์ด์คํ๋ row๋ฅผ ๋ณต์ํ์ง ์์ผ๋ฉฐ, JSON import๋ remap ์คํจ/๋๋ฝ๋
collection_id๋ฅผNULL๋ก ์ ๋ฆฌ - JSON import๊ฐ ์ ์ปฌ๋ ์ ์ ๋ง๋ค๋ฉด ๋ฉ์ธ ์๋จ ์ปฌ๋ ์ ํํฐ ์ต์ ์ ์ฆ์ ์๋ก๊ณ ์นจ
FILEํญ๋ชฉ์ ๋ณต์ ์ ์ ๋ชฉ๋ก/์์ธ/๋ฏธ๋ ์ฐฝ์์ ๋๋ฝ ๊ฒฝ๋ก(stale) ์ฌ๋ถ๋ฅผ ๋ฏธ๋ฆฌ ํ์- ๋ฐฑ์ ๋ฐ ๋ง์ด๊ทธ๋ ์ด์ ์ฉ์ด
- ๊ธ๋์ค๋ชจํผ์ฆ ๋์์ธ
- 5๊ฐ์ง ํ ๋ง: ๐ ๋คํฌ, โ๏ธ ๋ผ์ดํธ, ๐ ์ค์ , ๐ ํผํ, ๐ ๋ฏธ๋๋์
- v10.1: ํฅ์๋ ๋ง์ดํฌ๋ก ์ธํฐ๋์ (ํธ๋ฒ/ํฌ์ปค์ค ํจ๊ณผ)
- ์จ๋ณด๋ฉ ๊ฐ์ด๋๊ฐ ํฌํจ๋ ๋น ์ํ UI
- ํ๋กํ ํ ์คํธ ์๋ฆผ (์ฌ๋ผ์ด๋ ์ ๋๋ฉ์ด์ )
- ๋ฏธ๋ ์ฐฝ ๋ชจ๋ ์ง์
- ์๋จ ์ปฌ๋ ์ ํํฐ(์ ์ฒด/๋ฏธ๋ถ๋ฅ/๊ฐ๋ณ ์ปฌ๋ ์ )
| ๋จ์ถํค | ๊ธฐ๋ฅ |
|---|---|
Ctrl+Shift+V |
๋ฉ์ธ ์ฐฝ ํ์ |
Alt+V |
๋ฏธ๋ ์ฐฝ ํ ๊ธ |
Ctrl+Shift+Z |
๊ฐ์ฅ ์ต๊ทผ ๋ณต์ฌ ํญ๋ชฉ ์ฆ์ ๋ถ์ฌ๋ฃ๊ธฐ |
- OS: Windows 10/11
- Python: 3.10-3.14 (์์ค ์คํ/๋ก์ปฌ ๋น๋ ๊ฒ์ฆ ๊ธฐ์ค)
- ๋ฉ๋ชจ๋ฆฌ: 50MB ์ด์
Releases์์ SmartClipboard.exe ๋ค์ด๋ก๋
pip install -r requirements.txt
python "ํด๋ฆฝ๋ชจ๋ ๋งค๋์ .py"PyQt6>=6.4.0
keyboard>=0.13.5
cryptography>=41.0.0 # ๋ณด์ ๋ณด๊ดํจ ์ํธํ
requests>=2.28.0 # URL ์ ๋ชฉ ๊ฐ์ ธ์ค๊ธฐ
beautifulsoup4>=4.11.0 # HTML ํ์ฑ
qrcode[pil]>=7.3 # QR ์ฝ๋ ์์ฑ
Pillow>=9.0.0 # ์ด๋ฏธ์ง ์ฒ๋ฆฌ
python scripts/preflight_local.py
pyinstaller --clean smartclipboard.spec๊ฒฐ๊ณผ๋ฌผ: dist/SmartClipboard.exe (smartclipboard.spec๋ upx=True๋ก ์์ฒญํ์ง๋ง, ์ค์ ๋น๋ ์ถ๋ ฅ์ ๋ก์ปฌ UPX ๋๊ตฌ ๊ฐ์ฉ์ฑ์ ๋ฐ๋ผ Enabled ๋๋ Disabled๋ก ๋ฌ๋ผ์ง ์ ์์)
payload์ manifest๋ง ๋ค์ ์์ฑํด์ผ ํ ๋๋:
python scripts/build_legacy_payload.py --src smartclipboard_app/legacy_main_src.py --out smartclipboard_app/legacy_main_payload.marshal --smoke-import์ ๋ช
๋ น์ legacy_main_payload.marshal๊ณผ ํจ๊ป legacy_main_payload.manifest.json๋ ๊ฐฑ์ ํฉ๋๋ค.
python scripts/preflight_local.pypreflight_local.py๋ payload ์ฌ์์ฑ, payload smoke import, py_compile, unittest(test_payload_sync ํฌํจ)์ ์์ฐจ ์คํํฉ๋๋ค.
ํ์ฌ ํต์ฌ ํ๊ท ๋ฒ์์๋ test_core, test_ui_dialogs_widgets, test_payload_sync, test_legacy_loader, test_migration_collections, test_legacy_ui_contracts, test_signal_snapshot, test_public_surfaces๊ฐ ํฌํจ๋ฉ๋๋ค.
pyright๋ ๋ณ๋ ๋จ๊ณ์ด๋ฉฐ ๋ฃจํธ pyrightconfig.json ๊ธฐ์ค์ผ๋ก ํํ ์ ์ง๋ณด์ ๋์๋ง ๋ถ์ํฉ๋๋ค.
ํ์ฌ repo-wide pyright์๋ smartclipboard_core/db_parts/*.py mixin attribute typing ๋
ธ์ด์ฆ๊ฐ ๋จ์ ์์ผ๋ฏ๋ก, ๋ก์ปฌ ๊ฒ์ดํธ๋ preflight_local.py์ด๊ณ pyright๋ ๋ณ๊ฒฝ ํ์ผ ๊ธฐ์ค ๋ณด์กฐ ๊ฒ์ฆ์ผ๋ก ์ฌ์ฉํฉ๋๋ค.
Windows ๋ก์ปฌ ํ
์คํธ๋ ์์คํ
temp ๊ถํ ์ด์๋ฅผ ํผํ๊ธฐ ์ํด repo ๋ฃจํธ์ .tmp-unittest/ ํ์ ์์ ๋๋ ํฐ๋ฆฌ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
ํ์ ์ payload ์ฌ์์ฑ ๋จ๊ณ๋ฅผ ๊ฑด๋๋ฐ๋ ค๋ฉด:
python scripts/preflight_local.py --skip-payload-buildoptional dependency๊น์ง CI์ ๊ฐ์ ๊ฐ๋๋ก ํ์ธํ๋ ค๋ฉด ์ strict ์ปค๋งจ๋๋ฅผ ์ฌ์ฉํฉ๋๋ค. ๊ธฐ๋ณธ preflight_local.py๋ cryptography, requests, bs4, qrcode, PIL ๋๋ฝ์ ๊ฒฝ๊ณ ๋ง ์ถ๋ ฅํ๊ณ ๊ณ์ ์งํํ์ง๋ง, strict ๋ชจ๋์ CI๋ ์คํจ๋ก ์ฒ๋ฆฌํฉ๋๋ค.
python scripts/preflight_local.py --skip-payload-build --strict-optional-deps- ์ํฌํ๋ก์ฐ ํ์ผ:
.github/workflows/ci.yml - ์คํ ํ๊ฒฝ:
windows-latest - Python ๋งคํธ๋ฆญ์ค:
3.10,3.11,3.12,3.13 - ๊ฐ ๋งคํธ๋ฆญ์ค์์ ๋ค์์ ์ํํฉ๋๋ค.
- payload ๋น๋ + smoke import
python scripts/preflight_local.py --skip-payload-build --strict-optional-deps
pyright- ๋ฃจํธ
pyrightconfig.json์ด ๊ณต์ ๋ถ์ ๋ฒ์๋ฅผ ์ ์ํฉ๋๋ค. - ๊ธฐ๋ณธ ๋ฒ์์๋ ํํ ์ ์ง๋ณด์ ๋์(
ํด๋ฆฝ๋ชจ๋ ๋งค๋์ .py,smartclipboard_app/,smartclipboard_core/,tests/)๋ง ํฌํจ๋ฉ๋๋ค. - ๋ ๊ฑฐ์ ๋ณด๊ด๋ณธ
legacy/ํด๋ฆฝ๋ชจ๋ ๋งค๋์ (legacy).py์ ์์ค ์ค๋ ์ทsmartclipboard_app/legacy_main_src.py๋ ํธํ์ฑ ์ฐธ์กฐ์ฉ์ด๋ฏ๋ก ๊ธฐ๋ณธ ๋ถ์์์ ์ ์ธ๋ฉ๋๋ค.
| ๋จ์ถํค | ๊ธฐ๋ฅ |
|---|---|
Ctrl+F |
๊ฒ์์ฐฝ ํฌ์ปค์ค |
Ctrl+C |
์ ํ ํญ๋ชฉ ๋ณต์ฌ |
Ctrl+P |
๊ณ ์ /ํด์ ํ ๊ธ |
Ctrl+G |
๊ตฌ๊ธ ๊ฒ์ |
Enter |
๋ณต์ฌ ํ ๋ถ์ฌ๋ฃ๊ธฐ |
Delete |
์ ํ ํญ๋ชฉ ์ญ์ |
Escape |
์ฐฝ ์จ๊ธฐ๊ธฐ |
- URL ์ ๋ชฉ ๊ฐ์ ธ์ค๊ธฐ ๋น๋๊ธฐํ: ๋คํธ์ํฌ ์์ฒญ ์ UI ํ๋ฆฌ์ง ํ์ ์์ ํด๊ฒฐ
Workerํด๋์ค ๋ฐ ์ค๋ ๋ํ ๋์ (Background Processing)- ์ฆ๊ฐ์ ์ธ ํด๋ฆฝ๋ณด๋ ๋ฐ์์ฑ ๋ฐ ์ฒ๋ฆฌ ์ํ ์๊ฐํ (Fetching...)
- ์๋ DB ๋ฐฑ์
: ๋งค์ผ 1ํ
backups/ํด๋์ DB ์๋ ๋ฐฑ์ (์ต๊ทผ 7์ผ ๋ณด๊ด) - ํด๋ฆฝ๋ณด๋ ๋ชจ๋ํฐ ๋ฆฌ์ : ํธ๋ ์ด ๋ฉ๋ด > ๊ณ ๊ธ > ๋ชจ๋ํฐ ์ฌ์์ ๊ธฐ๋ฅ (์ฒด์ธ ๋๊น ํด๊ฒฐ)
- ์ด๋ฏธ์ง ํ์คํ ๋ฆฌ ์ ํ: ์ด๋ฏธ์ง ํญ๋ชฉ์ ์ต์ 20๊ฐ๋ง ์ ์งํ์ฌ DB ์ฉ๋ ์ต์ ํ
- ๊ฐ๋ ์ฑ ํฅ์: UI ๊ธฐ๋ณธ ํฐํธ๋ฅผ **'๋ง์ ๊ณ ๋(Malgun Gothic)'**์ผ๋ก ๋ณ๊ฒฝ
- ํ ์คํธ ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์์ญ ํฐํธ ๊ฐ๋ ์ฑ ๊ฐ์
- ๊ณ ์ ํญ๋ชฉ ์์ ๋ณ๊ฒฝ: ๋๋๊ทธ ์ ๋ฐ์ดํฐ ์์ค ๋ฒ๊ทธ ์์
DragDropMode๋ณ๊ฒฝ์ผ๋ก Qt ์๋ ํ ์ญ์ ๋ฐฉ์งeventFilter์ฌ์ค๊ณ (DragEnter/DragMove/Drop ๋ถ๋ฆฌ ์ฒ๋ฆฌ)
toggle_pin: ์ ๊ณ ์ ํญ๋ชฉpin_order์๋ ์ด๊ธฐํsoft_delete,restore_item: rollback ์ถ๊ฐadd_snippet,update_snippet,delete_snippet: rollback/return ์ถ๊ฐset_setting: rollback ์ถ๊ฐupdate_url_title: URL ์ ๋ชฉ ์บ์ ์ ์ฅ ๋ฉ์๋ ์ถ๊ฐ- ์ญ์ /๋ณต์ ์ ํด์งํต ๋ฉํ๋ฐ์ดํฐ(tags/note/bookmark/collection/pin/use_count) ๋ณด์กด
- ๊ณ ์ ํญ๋ชฉ ์์ ๊ฐฑ์ ์ ํธ๋์ญ์
์ผ๊ด ์ฒ๋ฆฌ๋ก ๋ณ๊ฒฝ(
update_pin_orders)
add_collection(): ์ปฌ๋ ์ ์์ฑget_collections(): ๋ชฉ๋ก ์กฐํupdate_collection(): ์์ delete_collection(): ์ญ์ (ํญ๋ชฉ ์ฐ๊ฒฐ ํด์ )assign_to_collection(): ํญ๋ชฉ ํ ๋น/ํด์ get_items_by_collection(): ์ปฌ๋ ์ ๋ณ ์กฐํ- ๋ฉ์ธ ์๋จ ์์ ํํฐ๋ก
์ ์ฒด/๋ฏธ๋ถ๋ฅ/์ปฌ๋ ์ ์ฆ์ ์กฐํ ์ง์
- ํซํค ์ค์ ์ ์ฅ ์ฆ์
register_hotkeys()์ฌ๋ฑ๋ก (์ฌ์์ ๋ถํ์) - ์๋ ๋ฐฑ์ ์ "์ฑ ์์ 1ํ"์์ "์ค์ง์ ์ผ 1ํ"๋ก ๋ณด๊ฐ (1์๊ฐ ์ฃผ๊ธฐ ๋ ์ง ๋ณ๊ฒฝ ๊ฐ์)
cleanup()์ดtimestamp ASC, id ASC๊ธฐ์ค์ผ๋ก ๊ฐ์ฅ ์ค๋๋ ํญ๋ชฉ๋ถํฐ ์ ๋ฆฌ๋๋๋ก ์์ - ์ ์ฒด ๊ธฐ๋ก ์ญ์ ๊ฐ ์๊ตฌ ์ญ์ ๊ฐ ์๋๋ผ ๊ณ ์ ์ ์ธ ํ ํด์งํต ์ด๋์ผ๋ก ๋์
- JSON ์ฌ-import ์ ์ปฌ๋ ์ ์ด๋ฆ์ ์ ๊ทํํด ๊ธฐ์กด ์ปฌ๋ ์ ์ ์ฌ์ฌ์ฉํ๊ณ ์ค๋ณต ์์ฑ์ ๋ฐฉ์ง
- CSV/Markdown ๋ด๋ณด๋ด๊ธฐ๋ JSON๊ณผ ๋์ผํ ๋ ์งยทํ์ ํํฐ๋ฅผ ์ ์ฉํ๊ณ ์ด๋ฏธ์ง ํญ๋ชฉ์ ํ๋ ์ด์คํ๋๋ก ๊ธฐ๋ก
- ์ค๋ํซ
shortcutUI์ ์ฑ ๋ด๋ถ ๋จ์ถํค ์คํ ๊ฒฝ๋ก๋ฅผ ์ฐ๊ฒฐ - ๋ณต์ฌ ๊ท์น/ํด๋ฆฝ๋ณด๋ ์ก์ ๋ค์ด์ผ๋ก๊ทธ์ ์์ฑยท์์ ยท์ญ์ ยท์ฐ์ ์์ ์ด๋์ ๋ชจ๋ ๋ฐ์
- ์ปฌ๋ ์ ๊ด๋ฆฌ ๋ค์ด์ผ๋ก๊ทธ๋ฅผ ์ถ๊ฐํ๊ณ ์ด๋ฆ ์ค๋ณต/๋น ๊ฐ ๊ฒ์ฆ์ ์ ์ฉ
- ๋ณด์ ๋ณด๊ดํจ ๋ง์คํฐ ๋น๋ฐ๋ฒํธ ๋ณ๊ฒฝ๊ณผ ๋ณตํธํ ํด๋ฆฝ๋ณด๋ 30์ด ์๋ ์ญ์ ๋ฅผ ์ถ๊ฐ
- ํซํค ์ ์ฅ ์คํจ ์ ์ด์ ๊ธ๋ก๋ฒ ํซํค ์ํ๋ก ๋กค๋ฐฑ๋๋๋ก ๋ณด๊ฐ
fetch_title์ก์ ๊ฒฝ๋ก๊ฐ ํ ์คํธ ์ ์ฒด๊ฐ ์๋ ์ฒซ URL๋ง ์ถ์ถํด ์ ๋ชฉ ์์ฒญํ๋๋ก ๋ณด๊ฐ- ๋น ๊ฒ์(
q == "")์์๋ ํ๊ทธ/๋ถ๋งํฌ/์ปฌ๋ ์ /๋ฏธ๋ถ๋ฅ ๋ณตํฉ ํํฐ๋ฅผ ๋์ ์ ์ฉํ๋๋ก ๊ฒ์ ๊ฒฝ๋ก ํตํฉ TrashDialog๋ค์ค ์ ํ(ExtendedSelection)์ ๋ช ์ํด ๋ฌธ์์ ์ค์ ๋์ ์ ํฉ์ฑ ํ๋ณด- ๋ฏธ๋ ์ฐฝ ๋๋ธํด๋ฆญ ๋ณต์ฌ ์
is_internal_copy๋ฅผ ์ค์ ํด ์๊ธฐ ์ฌ์์ง ๋ฃจํ ๋ฐฉ์ง update_always_on_top()์์ ์ฐฝ ๊ฐ์์ฑ ๋ณด์กด ๊ฐ๋ ์ ์ฉ (--minimized์์ ์์ ์ฑ ๊ฐ์ )- JSON ๋ง์ด๊ทธ๋ ์ด์
์
collections๋ฉํ๋ฐ์ดํฐ๋ฅผ ํฌํจํ๊ณ import ์ ์ปฌ๋ ์ ID remap ์ง์
add_snippet()์ ๋ฐํ์datetime๋๋ฝ์ ์์ ํด ์ ๊ท ์ค๋ํซ ์ ์ฅ ๊ฒฝ๋ก ๋ณต๊ตฌ- ๋์ผ ๋น์ด๋ฏธ์ง ํ
์คํธ๋ฅผ ๋ค์ ๋ณต์ฌํ๋ฉด ๊ธฐ์กด row์ ๋ฉํ๋ฐ์ดํฐ(tags/note/bookmark/collection/pin/use_count)๋ฅผ ์ ์งํ ์ฑ
timestamp/content/type๋ง ๊ฐฑ์ - ์ผ๋ฐ ํ์คํ ๋ฆฌ/๋ถ๋งํฌ/์ปฌ๋ ์
/๋ฏธ๋ถ๋ฅ/๋น ๊ฒ์ fallback์ unpinned ์ ๋ ฌ์
timestamp DESC, id DESC๋ก ํต์ผ - ๋ณด์ ๋ณด๊ดํจ ๋ณต์ฌ, ์ค๋ํซ ์ฌ์ฉ, URL ๋ณต์ฌ ๊ฒฝ๋ก์์
smartclipboard_app.ui.clipboard_guard.mark_internal_copy()๋ฅผ ํตํด ๋ด๋ถ ๋ณต์ฌ ํ๋๊ทธ๋ฅผ ๋จผ์ ์ธํ - JSON export/import๊ฐ
IMAGEํญ๋ชฉ์image_data_b64๋ก round-trip - pinned drag-drop helper์
Qt์ฐธ์กฐ ๋๋ฝ์ ์์
- ํ ์คํธ ํธ์ถ์
detail/duration/toast_typeํค์๋ ๊ธฐ์ค์ผ๋ก ์ ๋ฆฌํ๊ณ , URL ์ ๋ชฉ ์ฒ๋ฆฌ ์ค ์์ธ๊ฐ ๋๋clipboard.dataChanged๊ฐ ๋ฐ๋์ ์ฌ์ฐ๊ฒฐ๋๋๋ก ๋ณด๊ฐ Ctrl+Shift+Z๊ฐ ํ์ ์์๊ฐ ์๋ ์ค์ ์ต๊ทผ ๋ณต์ฌ ํญ๋ชฉ(timestamp DESC,id DESC)์ ๋ถ์ฌ๋ฃ๋๋ก ์ ํฉ์ฑ ์์ - ํ ์ด๋ธ ์ฌ์ฉ์ ์ ๋ ฌ์ด ๋ด๋ฆผ์ฐจ์์ด์ด๋ pinned-first ์ ์ฑ ์ด ๊นจ์ง์ง ์๋๋ก pinned/unpinned ๊ทธ๋ฃน์ ๋ถ๋ฆฌ ์ ๋ ฌ
- ์ปฌ๋ ์
์ญ์ ์
deleted_history.collection_id๋NULL๋ก ์ ๋ฆฌํ๊ณ , ๋ณต์ ์ ์ญ์ ๋ ์ปฌ๋ ์ ์ฐธ์กฐ๋ ์๋์ผ๋กNULL๋ณต์ - ๋ณด์ ๋ณด๊ดํจ์ ๋ง์คํฐ ๋น๋ฐ๋ฒํธ ๋ณ๊ฒฝ ์งํ ์ด๋ฆฐ ๋ค์ด์ผ๋ก๊ทธ์์๋ ์ต์ ์ํธ๋ฌธ์ ๋ค์ ์กฐํํด ๋ณต์ฌ ๋ฒํผ์ด ๊ณ์ ๋์ํ๋๋ก ์์
- ์ข
๋ฃ/๋ณต์ ์ ๋น๋๊ธฐ URL ์ ๋ชฉ worker ๊ฒฐ๊ณผ๊ฐ ๋ซํ DB๋ก ๋ค์ด์ค์ง ์๋๋ก
ClipboardActionManager.shutdown()๊ฒฝ๋ก๋ฅผ ์ถ๊ฐ
FILEํ์ ์ ์ถ๊ฐํด ๋ก์ปฌ ํ์ผ/ํด๋ ๋ณต์ฌ๋ฅผ ๋ค์ค ๊ฒฝ๋ก ํ๋์ ํ์คํ ๋ฆฌ ํญ๋ชฉ์ผ๋ก ์ ์ฅhistory.file_path๋ฅผ FILE ์ฒซ ๊ฒฝ๋ก ์ ์ฅ์ฉ์ผ๋ก ํ์ฑํํ๊ณ , ๋์ผ path ์งํฉ ์ฌ๋ณต์ฌ ์ ๊ธฐ์กด metadata๋ฅผ ์ ์งํ ์ฑ row๋ฅผ ๊ฐฑ์- paste-last/๋ฏธ๋ ์ฐฝ/์ ํ ๋ถ์ฌ๋ฃ๊ธฐ/๋๋ธํด๋ฆญ ๋ณต์ ๊ฒฝ๋ก๊ฐ
QMimeData + file URLํด๋ฆฝ๋ณด๋๋ฅผ ๋ค์ ๊ตฌ์ฑํ๋๋ก ๋ณด๊ฐ - ์ผ๋ถ ํ์ผ๋ง ๋จ์ ์์ผ๋ฉด ๋จ์ ๊ฒฝ๋ก๋ง ๋ณต์ํ๊ณ , ๋ชจ๋ ์ฌ๋ผ์ก์ผ๋ฉด ๊ฒฝ๊ณ ๋ง ํ์ํ๊ณ clipboard/paste๋ ๊ฑด๋๋ฆฌ์ง ์์
- JSON export/import๋
FILE์file_paths/file_path/newline content๋ฅผ ๋ชจ๋ ์ง์ํ๊ณ , CSV/Markdown์ ๊ฒฝ๋ก ๋ชฉ๋ก๋ง ๊ธฐ๋ก - CSV import๋
IMAGEํ๋ ์ด์คํ๋ row๋ฅผ ๊ฑด๋๋ฐ๊ณ , JSON import๋ ISO timestamp๋ฅผ ์๋ณธ ์๊ฐ ๊ธฐ์ค ์ฑ ํ์ค ์๊ฐ์ผ๋ก ์ ๊ทํํ๋ฉฐ ๊ณ ์collection_id๋ฅผNULL๋ก ์ ๋ฆฌ - ๋ณด์ ๋ณด๊ดํจ
unlock()์คํจ ์fernet/is_unlocked์ํ๋ฅผ ์์์ ์ผ๋ก ์ด๊ธฐํํด ์๋ชป๋ ์ฌ์๋ ํ ๋ฐ์ฏค ์ด๋ฆฐ ์ํ๊ฐ ๋จ์ง ์๋๋ก ์์
fetch_title์ URL ํํ ๋ฌธ์ฅ๋ถํธ๋ฅผ ์ ๊ฑฐํ๊ณ , ๋ก์ปฌ/์ฌ์ค/๋ฉํ๋ฐ์ดํฐ ์ฃผ์์ ๋น HTML ์๋ต์ ์ ๋ชฉ ์กฐํ ๋์์์ ์ ์ธ- ์ ํ๋ฒํธ ์๋ ํฌ๋งท ๋ฒ์๋ฅผ
02, ์ผ๋ฐ ์ง์ญ๋ฒํธ,0505,15xx/16xx/18xx๋ํ๋ฒํธ๊น์ง ํ์ฅ - ๋ณด์ ๋ณด๊ดํจ์ salt/verification ์ค์ ์ ํจ๊ป ์ ์ฅํ๊ณ , ์์ ์ํ์์๋ ์ ๊ธ ํ๋ฉด
Reset ๋ณด๊ดํจ์ผ๋ก ์ค์ ๊ฐ๊ณผ ์ ์ฅ ํญ๋ชฉ์ ์ด๊ธฐํ ๋ณต๊ตฌ - ๋ณต์ฌ ๊ท์น์
custom_replace๋ ๋น ๋ฌธ์์ด ์นํ์ ํ์ฉํด โ์ญ์ ์นํโ์ ์ฌ์ฉํ ์ ์๋๋ก ์ํ FILEํญ๋ชฉ์ ๋ชฉ๋ก/์์ธ/๋ฏธ๋ ์ฐฝ์์ ๋๋ฝ ํ์ผ ์๋ฅผ ๋ฏธ๋ฆฌ ๋ณด์ฌ ์ฃผ์ด ๋ถ์ฌ๋ฃ๊ธฐ ์ ์ stale ์ํ๋ฅผ ํ์ธ ๊ฐ๋ฅ- JSON ๋ง์ด๊ทธ๋ ์ด์ ๋ฌธ๊ตฌ๋ฅผ ์ค์ ๋ฒ์(ํ์คํ ๋ฆฌ ๋ฉํ๋ฐ์ดํฐ + ์ปฌ๋ ์ ) ๊ธฐ์ค์ผ๋ก ์ ๋ฆฌํ๊ณ , ๊ด๋ จ ํ๊ท ํ ์คํธ๋ฅผ ์ถ๊ฐ
- ๋ฏธ๋ ํด๋ฆฝ๋ณด๋ ์ฐฝ On/Off ์ต์ ์ถ๊ฐ (์ค์ > ์ผ๋ฐ > ๋ฏธ๋ ์ฐฝ)
- ๋นํ์ฑํ ์
Alt+V๋จ์ถํค ๋ฑ๋ก ํด์ - ์ค์ ๋ณ๊ฒฝ ์ ์ฌ์์ ์์ด ์ฆ์ ์ ์ฉ
- Google ๊ฒ์ URL ์ธ์ฝ๋ฉ ์ถ๊ฐ (ํน์๋ฌธ์ ์ฒ๋ฆฌ)
- Import ์ ํ์ ์ ํจ์ฑ ๊ฒ์ฆ (์๋ชป๋ ํ์ ์๋ ๋ณต๊ตฌ)
- ํด๋ฆฝ๋ณด๋ ๊ฐ์ง ๋๋ฐ์ด์ค ๊ฐ์ (๋น ๋ฅธ ์ฐ์ ๋ณต์ฌ ์ ์ค๋ณต ํธ์ถ ๋ฐฉ์ง)
export_json๋ ์ง ํํฐ๋ง ๊ธฐ๋ฅ ๊ตฌํ (date_fromํ๋ผ๋ฏธํฐ)
TYPE_ICONS์์ ํต์ผ (3๊ฐ ์์น์์ ์ค๋ณต ์ ๊ฑฐ)empty_trash()rollback ์ถ๊ฐ (DB ์ผ๊ด์ฑ ๋ณด์ฅ)
- ๋ง์คํฐ ๋น๋ฐ๋ฒํธ ๊ฐ๋ ๊ฒ์ฆ ์ถ๊ฐ (8์ ์ด์, ์ซ์+ํน์๋ฌธ์ ํ์)
- URL ์ ๋ชฉ ๊ฐ์ ธ์ค๊ธฐ ํ์์์ ์ค์ (๊ธฐ๋ณธ 5์ด)
- ์ก์ ํจํด ์ ๊ท์ ์ ํจ์ฑ ๊ฒ์ฆ
- ํด์งํต ๋ค์ด์ผ๋ก๊ทธ ๋ค์ค ์ ํ ์ง์
- ์ ํ ํญ๋ชฉ ์ผ๊ด ๋ณต์/์ญ์ ๊ธฐ๋ฅ
- ๋น ํด์งํต ๊ฒฝ๊ณ ๋ฉ์์ง ๊ฐ์
- ์ค๋ํซ ์์ ๋ค์ด์ผ๋ก๊ทธ ์ถ๊ฐ
- ๋๋ธํด๋ฆญ์ผ๋ก ์ค๋ํซ ํธ์ง ๊ฐ๋ฅ
- ํธ์ง ์ ์นดํ ๊ณ ๋ฆฌ ๋ณ๊ฒฝ ์ง์
- ํซํค ๋ฑ๋ก/ํด์ ์๋ฌ ํธ๋ค๋ง ๊ฐํ
- ์ฑ ์ข ๋ฃ ์ ์์ ํ ํซํค ํด์
- ์ถฉ๋ ํซํค ๊ฐ์ง ๋ฐ ๊ฒฝ๊ณ
- ๋ง๋ฃ๋ ์์ ํญ๋ชฉ ์๋ ์ ๋ฆฌ (1์๊ฐ ์ฃผ๊ธฐ)
- ํด์งํต ๋ง๋ฃ ํญ๋ชฉ ์๋ ์ญ์ (1์๊ฐ ์ฃผ๊ธฐ)
QTimer๊ธฐ๋ฐ ๋ฆฌ์์ค ์ ๋ฆฌ
load_data()UI ์ผ๊ด ๋ ๋๋ง (setUpdatesEnabled)hashlib๋ชจ๋ ๋ ๋ฒจ import ์ด๋TYPE_ICONS์์ ์ถ์ถ๋ก ๋ฃจํ ๋ด ๋์ ๋๋ฆฌ ์์ฑ ์ ๊ฑฐ- DB ์ธ๋ฑ์ค ์ถ๊ฐ (pinned, type, timestamp, bookmark)
- ๋ฒํผ ํธ๋ฒ/ํฌ์ปค์ค ํจ๊ณผ ๊ฐํ (2px ํ ๋๋ฆฌ, ํฌ์ปค์ค ๋ง)
- ํ ์ด๋ธ ํ ํธ๋ฒ ์ ์ข์ธก ํ๋ผ์ด๋จธ๋ฆฌ ์ปฌ๋ฌ ๋ณด๋
- ์ ํ ํญ๋ชฉ ํฐํธ ๊ตต๊ธฐ ๊ฐ์กฐ
- ๋น ์ํ์์ ๋จ์ถํค ๊ฐ์ด๋ ํฌํจ ์จ๋ณด๋ฉ UI
SecureVaultManager.unlock()์์ธ ์ฒ๋ฆฌ ์ธ๋ถํUI_TEXTS์์ ์ถ๊ฐ (๋ค๊ตญ์ด ์ง์ ๋๋น)
smartclipboard/
โโโ ํด๋ฆฝ๋ชจ๋ ๋งค๋์ .py # ์ธ๋ถ ํธํ ํ์ฌ๋
โโโ pyrightconfig.json # Pylance/pyright ๋ถ์ ๋ฒ์
โโโ requirements.txt # Python ์์กด์ฑ
โโโ smartclipboard.spec # PyInstaller ๋น๋ ์ค์
โโโ smartclipboard_app/
โ โโโ bootstrap.py
โ โโโ legacy_main.py # legacy payload loader
โ โโโ legacy_payload.py # payload manifest/hash helper
โ โโโ legacy_main_payload.marshal # ๋ฐํ์ payload
โ โโโ legacy_main_payload.manifest.json # Python/source sync manifest
โ โโโ features/ # ๊ธฐ๋ฅ ๋๋ฉ์ธ ๊ตฌํ์ฒด
โ โโโ managers/
โ โโโ ui/
โ โโโ clipboard_guard.py # internal copy flag helper
โ โโโ mainwindow_parts/ # legacy ํธํ shim (์ค๊ตฌํ์ features/)
โโโ smartclipboard_core/
โ โโโ actions.py
โ โโโ automation/ # ClipboardActionManager ๋ถ๋ฆฌ ๊ตฌํ
โ โโโ database.py
โ โโโ db_parts/ # mixin facade + ํ์ subpackage
โ โโโ worker.py
โโโ tests/
โโโ legacy/ # ์ฐธ์กฐ์ฉ ๋ ๊ฑฐ์ ๋ณด๊ด๋ณธ
| ํ ์ด๋ธ | ์ฉ๋ |
|---|---|
history |
ํด๋ฆฝ๋ณด๋ ํ์คํ ๋ฆฌ ์ ์ฅ |
snippets |
ํ ์คํธ ์ค๋ํซ ์ ์ฅ |
settings |
์ฑ ์ค์ (ํ ๋ง, ํซํค ๋ฑ) |
copy_rules |
๋ณต์ฌ ๊ท์น |
secure_vault |
์ํธํ๋ ๋ณด์ ํญ๋ชฉ |
clipboard_actions |
์๋ํ ์ก์ ๊ท์น |
collections |
์ปฌ๋ ์ ์ ๋ณด |
deleted_history |
ํด์งํต (7์ผ ๋ณด๊ด) |
- ๋น ๋ฅธ ์ ๊ทผ:
Ctrl+Shift+V๋ก ์ธ์ ๋ ํ์คํ ๋ฆฌ ํ์ธ - ๋ฏธ๋ ๋ชจ๋:
Alt+V๋ก ์์ ์ฐฝ์์ ๋น ๋ฅด๊ฒ ํญ๋ชฉ ์ ํ - ๊ณ ์ ํญ๋ชฉ: ์์ฃผ ์ฐ๋ ํ ์คํธ๋ ๐ ๊ณ ์ ํ์ฌ ์๋จ์ ์ ์ง
- ํ๊ทธ ํ์ฉ: ๊ด๋ จ ํญ๋ชฉ๋ผ๋ฆฌ ํ๊ทธ๋ก ๋ถ๋ฅ
- ๋ณด์ ๋ณด๊ดํจ: ๋น๋ฐ๋ฒํธ, API ํค ๋ฑ ๋ฏผ๊ฐ ์ ๋ณด๋ ์ํธํ ๋ณด๊ด
- ์ค๋ํซ: ์์ฃผ ์ฌ์ฉํ๋ ์ด๋ฉ์ผ ์๋ช , ํ ํ๋ฆฟ ๋ฑ์ ์ค๋ํซ์ผ๋ก ์ ์ฅ
smartclipboard_app/legacy_main.py๋ ๋ ๊ฑฐ์ ๋ฐํ์์ ๋ก๋ํ๋ ํ์ด๋ธ๋ฆฌ๋ ๋ชจ๋์ ๋๋ค.- ๊ธฐ๋ณธ๊ฐ(๊ถ์ฅ): payload ๋ชจ๋ (
smartclipboard_app/legacy_main_payload.marshal) (env: ๋ฏธ์ค์ ๋๋SMARTCLIPBOARD_LEGACY_IMPL=payload) - payload๋
legacy_main_payload.manifest.json์ผ๋ก ํ์ฌ Python minor/source hash๋ฅผ ๊ฒ์ฆํฉ๋๋ค. - payload ๋ก๋ฉ ์คํจ(ํ์ผ ๋๋ฝ/ํ์ฑ ์คํจ/manifest ๋ถ์ผ์น/์คํ ์คํจ) ์
legacy_main_src.py๋ก ์๋ ํด๋ฐฑํ๋ฉฐ,LEGACY_IMPL_ACTIVE/LEGACY_IMPL_FALLBACK_REASON์์๋ก ์ํ๋ฅผ ํ์ธํ ์ ์์ต๋๋ค. - ์์ค ๋ชจ๋(์ ์ ๋ถ์/ํด๋์ค/์๊ทธ๋ ์ถ์ ์ฉ): env
SMARTCLIPBOARD_LEGACY_IMPL=src - ๋ณต์๋ ์๋ณธ ์์ค:
smartclipboard_app/legacy_main_src.py(์๋ณธ:legacy/ํด๋ฆฝ๋ชจ๋ ๋งค๋์ (legacy).py) - Pylance/pyright๋ ๋ฃจํธ
pyrightconfig.json์ ๊ธฐ์ค์ผ๋ก ํํ ์ ์ง๋ณด์ ์ฝ๋๋ง ๊ฒ์ฌํฉ๋๋ค. - ์ง์
clipboard.setText()๋ฅผ ํธ์ถํ๋ ๊ฒฝ๋ก๋smartclipboard_app.ui.clipboard_guard.mark_internal_copy()๋ฅผ ๋จผ์ ๊ฑฐ์ณ ์๊ธฐ ์ฌ์์ง ๋ฃจํ๋ฅผ ํผํฉ๋๋ค. - JSON export/import๋
IMAGEํญ๋ชฉ์ฉimage_data_b64round-trip์ ์ง์ํ๊ณ , CSV/Markdown์ ์ด๋ฏธ์ง BLOB๋ฅผ ์๋์ ์ผ๋ก ์ ์ธํฉ๋๋ค. FILEํญ๋ชฉ์ ๊ฒฝ๋ก ๋ชฉ๋ก ์ค์ฌ์ผ๋ก ๋์ํ๋ฉฐ, JSON์file_paths/file_path, CSV/Markdown์ newline path content๋ฅผ ์ฌ์ฉํฉ๋๋ค.FILEํญ๋ชฉ์ ๋ณต์ ์์ ๋ฟ ์๋๋ผ ๋ชฉ๋ก/์์ธ/๋ฏธ๋ ์ฐฝ์์ ๋๋ฝ ๊ฒฝ๋ก ์๋ฅผ ๋จผ์ ๋ณด์ฌ์ฃผ๋ฉฐ, ์ผ๋ถ๋ง ๋จ์ ์์ผ๋ฉด ๋ถ๋ถ ๋ณต์ ์ ์ฑ ์ ์ ์งํฉ๋๋ค.- import ๋ฌด๊ฒฐ์ฑ ์ ์ฑ
์ CSV ์ด๋ฏธ์ง ํ๋ ์ด์คํ๋๋ ๋ณต์ํ์ง ์๊ณ , JSON์์ ๋งคํ ๋ถ๊ฐ
collection_id๋NULL, ๋นํ์ค timestamp๋ ์ ๊ทํ ๋๋ import ์๊ฐ์ผ๋ก ๋์ฒดํฉ๋๋ค. fetch_title์ ์ฒซ URL๋ง ๋์์ผ๋ก ํ๋ฉฐ, ๋ก์ปฌ/์ฌ์ค/๋ฉํ๋ฐ์ดํฐ ์ฃผ์ ์ฐจ๋จ๊ณผ HTML ์๋ต ์ ํ์ ๊ธฐ๋ณธ ์ ์ฑ ์ผ๋ก ์ ์งํฉ๋๋ค.- ๋ณด์ ๋ณด๊ดํจ์
vault_salt์vault_verification์ด ํจ๊ป ์์ด์ผ ์ ์ ๊ตฌ์ฑ์ผ๋ก ๊ฐ์ฃผํ๋ฉฐ, ์์ ์ํ๋ Reset ๋ณต๊ตฌ ํ๋ฆ์ ํตํด ์ ๋ฆฌํฉ๋๋ค. - ๊ธฐ์กด ๋ชจ๋๋ฌ ๋ ์ด์์ README๋
legacy/README (modular).md์ ๋ณด๊ด๋์ด ์์ต๋๋ค.
- Windows ์ ์ฉ (macOS/Linux ๋ฏธ์ง์)
- ์ผ๋ถ ์ ํ๋ฆฌ์ผ์ด์ ์์ ๊ธ๋ก๋ฒ ํซํค ์ถฉ๋ ๊ฐ๋ฅ
- ์ด๋ฏธ์ง ํ์คํ ๋ฆฌ๋ ํฌ๊ธฐ์ ๋ฐ๋ผ DB ์ฉ๋ ์ฆ๊ฐ
- ์ค๋ํซ ๋จ์ถํค๋ ์ฑ์ด ํ์ฑํ๋ ์ํ์์๋ง ๋์ํ๋ ์ฑ ๋ด๋ถ ๋จ์ถํค๋ก ์ ํ
๋ฒ๊ทธ ๋ฆฌํฌํธ, ๊ธฐ๋ฅ ์ ์, PR ํ์ํฉ๋๋ค!
MIT License
ยฉ 2025-2026
ExportImportManager๊ณต๊ฐ ๋ฉ์๋๋ ๊ณ์int๋ฅผ ๋ฐํํ๊ณ , ์์ธ ๊ฒฐ๊ณผ๋last_import_report/last_export_report์ ๊ธฐ๋ก๋ฉ๋๋ค.- JSON/CSV import๋ ์์ ์ ์
backups/pre_import_YYYYMMDD_HHMMSS.db๋ฐฑ์ ์ ๋ง๋ค๊ณ ํ์ผ ๋จ์ ๋จ์ผ ํธ๋์ญ์ ์ผ๋ก ๋ฐ์๋์ด, ์ค๊ฐ ์คํจ ์ ์ ์ฒด rollback ๋ฉ๋๋ค. search_items()๋ FTS-first ์ ์ฑ ์ ์ ์งํ๋ฉด์ FTS 0๊ฑด์ผ ๋๋ง LIKE ๋ณด์ ๊ฒ์์ ์ํํฉ๋๋ค._last_search_fallback์ ์ค์ FTS ์ค๋ฅ์ผ ๋๋ง UI ๊ฒฝ๊ณ ์ฉ์ผ๋ก ์ผ์ง๋๋ค.ClipboardActionManager๋ ์ ์ฉQThreadPool(maxThreadCount=4)๊ณผ URL ๊ธฐ์ค in-flight dedupe/cache๋ฅผ ์ฌ์ฉํ๊ณ , ๋ฆ๊ฒ ๋์ฐฉํ title ๊ฒฐ๊ณผ๋ ํ์ฌ row์ ์ฒซ URL์ด ์ฌ์ ํ ๊ฐ์ ๊ฒฝ์ฐ์๋ง ์ ์ฅํฉ๋๋ค.history.file_signature์ปฌ๋ผ๊ณผ ์ธ๋ฑ์ค๋ฅผ ์ฌ์ฉํดFILE์ค๋ณต ํ๋ณ์ ์ ์ฒด row ์ํ ๋์ canonicalized path signature lookup์ผ๋ก ์ฒ๋ฆฌํฉ๋๋ค.- ๋ณด์ ๋ณด๊ดํจ ๋ณตํธํ ํ ์คํธ๋ ํ๋ก์ธ์ค ๋ด armed clipboard state๋ก ์ถ์ ๋๋ฉฐ, 30์ด ์กฐ๊ฑด๋ถ clear์ ์ฑ ์ข ๋ฃ ์ ์ฆ์ clear๋ฅผ ๋ชจ๋ ์ํํฉ๋๋ค.
- ์ค์ ์ ์ฅ ์
mini_window_enabled๋ณ๊ฒฝ์ผ๋ก ํซํค ์ฌ๋ฑ๋ก์ด ์คํจํ๋ฉด ๊ทธ ์ค์ ๋ง ๋๋๋ฆฌ๊ณ ์ค์ _last_hotkey_error๋ฅผ ๊ฒฝ๊ณ ๋ก ๋ ธ์ถํฉ๋๋ค. smartclipboard.spec์ ์ด๋ฒ ์์ ํ ๊ธฐ์ค์ผ๋ก payload manifest(legacy_main_payload.manifest.json)๊น์ง ํฌํจํ๋๋ก ์ ๋ฆฌ๋์ด ์์ผ๋ฉฐ, ๋ณ๋ ์ถ๊ฐ hidden import ์ฆ์ค์ ํ์ํ์ง ์์ต๋๋ค.
ClipboardActionManager์ ๋๊ธฐ ํ ์คํธ ์ก์ (format_phone,format_email,transform)์ ์์ฐจ์ ์ผ๋ก working text๋ฅผ ๊ฐฑ์ ํ๋ฉฐ,fetch_title์ ๋๊ธฐ ์นํ์ด ๋๋ ์ต์ข ํ ์คํธ์์ URL์ ๋ค์ ์ถ์ถํฉ๋๋ค.- ์ก์
๊ฒฐ๊ณผ๊ฐ ํ
์คํธ ์นํ์ด๋ฉด history row(
content/type/url_title/file_path/file_signature)์ clipboard๋ฅผ ํจ๊ป ๊ฐฑ์ ํด UI/์ ์ฅ์/์ค์ clipboard๊ฐ ๊ฐ์ ๊ฐ์ ์ ์งํฉ๋๋ค. - JSON import๋ก ์ปฌ๋ ์
์ด ์ถ๊ฐ๋๋ฉด
refresh_collection_filter_options()๋ฅผ ๋จผ์ ํธ์ถํด ๋ฉ์ธ ์๋จ ํํฐ๊ฐ ์ฆ์ ์ต์ ๋ชฉ๋ก์ ๋ฐ์ํฉ๋๋ค. - ๊ฒ์ ๊ฒฐ๊ณผ๋ query๊ฐ ์์ ๋ DB/FTS relevance ์์๋ฅผ ๊ธฐ๋ณธ์ผ๋ก ์ ์งํ๊ณ , ์ฌ์ฉ์๊ฐ ํค๋ ์ ๋ ฌ์ ์ง์ ๋ฐ๊พผ ๊ฒฝ์ฐ์๋ง client-side sort override๋ฅผ ์ ์ฉํฉ๋๋ค.
- ๊ฒ์ 0๊ฑด/๋น ํ์คํ ๋ฆฌ ๊ฒฝ๋ก์์๋
update_status_bar(0)์ ํธ์ถํด ์ด์ ์นด์ดํธ๊ฐ ๋จ์ง ์๋๋ก ์ ์งํฉ๋๋ค.
smartclipboard_app/legacy_main_src.py์MainWindow๊ณต๊ฐ ๋ฉ์๋ ์๊ทธ๋์ฒ๋ ์ ์ง๋ฉ๋๋ค.- ์ค์ ๊ตฌํ์
smartclipboard_app/features/๋๋ฉ์ธ ํจํค์ง๋ก ์ด๋ํ๊ณ ,smartclipboard_app/ui/mainwindow_parts/๋ import ํธํ์ฉ shim์ผ๋ก ์ ์ง๋ฉ๋๋ค.features/settings: ํ ๋ง/QSS/controllerfeatures/shell_ui: ๋ฉ์ธ ๋ ์ด์์, drag-drop, UI controllerfeatures/history: ๋ฉ๋ด/ํ ์ด๋ธ/view/controllerfeatures/clipboard: runtime pipeline/controllerfeatures/tray_hotkey: ์์คํ ํธ๋ ์ด/ํซํค/controllerfeatures/shell: ์ํ๋ฐ/์ ๋ฆฌ/์ข ๋ฃ controller
- ์๊ทธ๋ ์ค๋
์ท ๊ฒ์ฆ(
scripts/refactor_signal_snapshot.py,tests/test_signal_snapshot.py)์legacy_main_src.py์ shim+feature ๊ตฌํ ํ์ผ์ ํจ๊ป ์ค์บํฉ๋๋ค. - ๋ก์ปฌ ์ฌ์ ๊ฒ์ฆ(
scripts/preflight_local.py)์py_compile๋จ๊ณ๋ui/**/*.py,features/**/*.py,db_parts/**/*.py,automation/**/*.py๋ฅผ ์ฌ๊ท ํฌํจํฉ๋๋ค.
- ์คํ/๋น๋/๊ฒ์ฆ ๊ธฐ์ค ๋ฌธ์๋ ๋ฃจํธ
README.md์ด๋ฉฐ,claude.md,.gemini/GEMINI.md,legacy/README (modular).md๋ ๋์ผ ๊ธฐ์ค์ ๋ฐ๋ฆ ๋๋ค. - ๊ถ์ฅ ํ๊ท ํ
์คํธ ๊ธฐ์ค์
test_core,test_ui_dialogs_widgets,test_payload_sync,test_legacy_loader,test_migration_collections,test_legacy_ui_contracts,test_signal_snapshot,test_public_surfaces์ ๋๋ค. - PyInstaller ๊ธฐ์ค(
smartclipboard.spec)์ payload ๋ฐ์ดํฐ(legacy_main_payload.marshal)์ payload manifest(legacy_main_payload.manifest.json)๋ฅผ ํจ๊ป ํฌํจํ๊ณ ,smartclipboard_core,smartclipboard_app.ui.mainwindow_partsํ์ ๋ชจ๋์ hidden import๋ก ์๋ ์์งํ๋ฉฐ, payload์์ ์ง์ ์ฐธ์กฐํ๋ ๋ํ์์ ๋ชจ๋(smartclipboard_app.ui.dialogs.collectionsํฌํจ)์ ๋ช ์์ ์ผ๋ก ์ ์งํฉ๋๋ค. - 2026-04-13 ๊ธฐ์ค ์ถ๊ฐ ํจํค์ง ์์ฐ์ payload manifest 1๊ฑด์ด๋ฉฐ, ํ์ฌ spec์ ๋ฐ์๋์ด ์์ต๋๋ค.
smartclipboard_core/database.pyis now a composition entrypoint.- Database implementation now keeps flat facades in
smartclipboard_core/db_parts/*.pyand subpackages in:db_parts/search/db_parts/automation/db_parts/catalog/db_parts/retention/
smartclipboard_core/actions.pyis a public facade; implementation lives insmartclipboard_core/automation/.smartclipboard_app/managers/export_import.py/secure_vault.pyare public facades; implementations live insmartclipboard_app/features/import_export/andsmartclipboard_app/features/vault/.scripts/preflight_local.pynow compiles bothmainwindow_parts/*.pyanddb_parts/*.py.- Added surface-guard test:
tests/test_public_surfaces.pyand baselinetests/baseline/clipboarddb_public_methods.txt.
legacy_main_src.MainWindownow composes feature controllers (clipboard,history/table,tray_hotkey,lifecycle,settings,shell_ui) while preserving public method signatures.smartclipboard_app/features/shared/state.pyprovidesWindowState,WindowServices,WindowWidgetsbundles andbind_window_facets()for controller synchronization.smartclipboard_app/ui/controllers/*.pyandui/mainwindow_parts/*.pyremain compatibility layers so external imports and payload contracts stay intact.smartclipboard.specnow collectssmartclipboard_app.featuresandsmartclipboard_core.automationsubmodules as hidden imports in addition to the legacy shim modules.