# ๐Ÿ“š Mana Viewer (๋งˆ๋‚˜ ๋ทฐ์–ด) **Mana Viewer**๋Š” ์›นํˆฐ ๋ฐ ๋งŒํ™” ์‚ฌ์ดํŠธ๋ฅผ ๊ด‘๊ณ  ์—†์ด ์พŒ์ ํ•˜๊ฒŒ ๊ฐ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐ์Šคํฌํ†ฑ ์ „์šฉ ๋ทฐ์–ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ž…๋‹ˆ๋‹ค. Tauri v2(Rust)์™€ React + TypeScript ๊ธฐ๋ฐ˜์œผ๋กœ ์ œ์ž‘๋˜์–ด ๊ฐ€๋ณ๊ณ  ๋น ๋ฅด๊ฒŒ ๋™์ž‘ํ•˜๋ฉฐ, ํ…œํ”Œ๋ฆฟ ๊ธฐ๋ฐ˜ ์‚ฌ์ดํŠธ ํŒŒ์‹ฑ ๋ฐ 0์ดˆ ์‚ฌ์ „ ๋กœ๋”ฉ ์‹œ์Šคํ…œ์„ ํƒ‘์žฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. --- ## โœจ ์ฃผ์š” ๊ธฐ๋Šฅ - **๐ŸŒ ํ…œํ”Œ๋ฆฟ ๊ธฐ๋ฐ˜ ์‚ฌ์ดํŠธ ํŒŒ์‹ฑ ์‹œ์Šคํ…œ (SiteProfile)** - JSON ํ…œํ”Œ๋ฆฟ ํ”„๋กœํ•„์„ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์ดํŠธ๋ณ„ CSS ์…€๋ ‰ํ„ฐ, ์ด๋ฏธ์ง€ ์†์„ฑ, ์•ˆํ‹ฐ๋ด‡ ์„ค์ •์„ ๋…๋ฆฝ์ ์œผ๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. - ์™€์ผ๋“œ์นด๋“œ ๋„๋ฉ”์ธ ๋งค์นญ(`newtoki*.org`, `manatoki*.net` ๋“ฑ)์œผ๋กœ ๋„๋ฉ”์ธ ๋ณ€๊ฒฝ ์‹œ์—๋„ ์ž๋™ ์ ์‘ํ•ฉ๋‹ˆ๋‹ค. - ์ธ์•ฑ JSON ์—๋””ํ„ฐ ์ง€์›์œผ๋กœ ์ฝ”๋“œ ์ˆ˜์ • ์—†์ด ์ƒˆ ์‚ฌ์ดํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ํŽธ์ง‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. - **โšก ๋ฐฑ๊ทธ๋ผ์šด๋“œ 0์ดˆ ๋‹ค์Œ ํ™” ์‚ฌ์ „ ๋กœ๋”ฉ (Pre-harvest & Preload)** - ํ˜„์žฌ ์ฑ•ํ„ฐ๋ฅผ ์ฝ๋Š” ๋™์•ˆ ๋‹ค์Œ ์ฑ•ํ„ฐ์˜ ์ด๋ฏธ์ง€ ๋ฐ ์ •๋ณด๋“ค์„ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋ฏธ๋ฆฌ ์ˆ˜์ง‘ํ•˜๊ณ  ์ด๋ฏธ์ง€๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์บ์‹ฑํ•˜์—ฌ ๋Œ€๊ธฐ ์‹œ๊ฐ„ ์—†์ด 0์ดˆ ์ฆ‰์‹œ ์ „ํ™˜๋ฉ๋‹ˆ๋‹ค. - **๐Ÿท๏ธ ์Šค๋งˆํŠธ ํ™”์ˆ˜ & ์‹œ์ฆŒ ์ •๋ฐ€ ํŒŒ์‹ฑ** - ๋งŒํ™” ์ œ๋ชฉ์—์„œ `150ํ™”`, `1100-1ํ™”`, `1100.5ํ™”`, `2๊ธฐ 15ํ™”`, `์‹œ์ฆŒ2 15ํ™”`, `EP.150` ๋“ฑ์˜ ํ™”์ˆ˜/์‹œ์ฆŒ ์ •๋ณด๋ฅผ ์ž๋™ ์ถ”์ถœํ•˜์—ฌ ๋ณด๋ผ์ƒ‰ ํ•˜์ด๋ผ์ดํŠธ ๋ฐฐ์ง€๋กœ ๋ช…ํ™•ํ•˜๊ฒŒ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. - **๐Ÿ“– ๊ณ ๋„ํ™”๋œ ๋งŒํ™” ๋ทฐ์–ด ๊ธฐ๋Šฅ** - **๋‹จ๋ฉด/์–‘๋ฉด ๋ณด๊ธฐ**: 1์žฅ์”ฉ ๋ณด๊ธฐ ๋ฐ 2์žฅ์”ฉ ํŽผ์ณ๋ณด๊ธฐ ๋ชจ๋“œ ์ง€์›. - **ํ†ตํŽ˜์ด์ง€(Wide ์ด๋ฏธ์ง€) ์ž๋™ ๊ฐ์ง€**: ์–‘๋ฉด ๋ชจ๋“œ ์ค‘ ๊ฐ€๋กœ๊ฐ€ ๋„“์€ ๋งŒํ™” ํŽ˜์ด์ง€๋Š” 1์žฅ ๋‹จ๋… ์Šฌ๋กฏ์œผ๋กœ ์ž๋™ ์žฌ๋ฐฐ์น˜. - **์ฝ๊ธฐ ๋ฐฉํ–ฅ ์„ ํƒ**: ์ขŒโ†’์šฐ (LTR - ํ•œ๊ตญ ์›นํˆฐ) ๋ฐ ์šฐโ†’์ขŒ (RTL - ์ผ๋ณธ ๋งŒํ™”). - **์ด๋ฏธ์ง€ ๊ฐ•์ œ ํ™•๋Œ€ (Allow Upscale)**: ์ฐฝ ํฌ๊ธฐ์— ๋งž์ถฐ ์›๋ณธ๋ณด๋‹ค ํฌ๊ฒŒ ํ™•๋Œ€ํ•˜๋Š” ์˜ต์…˜ ์ œ๊ณต. - **๋ฐฐ๊ฒฝ์ƒ‰ ์„ค์ •**: ๊ฒ€์ • / ์–ด๋‘์šด ํšŒ์ƒ‰ / ํฐ์ƒ‰ ๋ฐฐ๊ฒฝ ์„ ํƒ. - **๐Ÿ”” ์ฑ•ํ„ฐ ์ „ํ™˜ & ๊ฒฝ๊ณ  ํ† ์ŠคํŠธ** - ๋‹ค์Œ ํ™” ์ „ํ™˜ ์‹œ ์ƒ๋‹จ ์ค‘์•™์— ์ฑ•ํ„ฐ ์ œ๋ชฉ ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€ ๋…ธ์ถœ. - ์ตœ์‹ ํ™” ๋˜๋Š” ์ฒซ ํ™”์—์„œ ์ด๋™ ์‹œ `โš ๏ธ ๋งˆ์ง€๋ง‰ ํ™”์ž…๋‹ˆ๋‹ค`, `โš ๏ธ ์ฒซ ๋ฒˆ์งธ ํ™”์ž…๋‹ˆ๋‹ค` ์•Œ๋ฆผ ํ‘œ์‹œ. - **๐Ÿ“œ ์ตœ๊ทผ ์ฝ์€ ๋งŒํ™” ๊ธฐ๋ก ๊ด€๋ฆฌ** - ์ตœ๊ทผ ์ฝ์€ ๋งŒํ™” ์ œ๋ชฉ, ํ™”์ˆ˜, URL, ์ฝ์€ ์‹œ๊ฐ„ ์ž๋™ ์ €์žฅ. - ํ•ญ๋ชฉ๋ณ„ ๊ฐœ๋ณ„ ์‚ญ์ œ(`โœ•`) ๋ฐ ์ „์ฒด ๊ธฐ๋ก ์‚ญ์ œ ๊ธฐ๋Šฅ ์ œ๊ณต. --- ## ๐Ÿ› ๏ธ ์‚ฌ์ „ ์š”๊ตฌ ์‚ฌํ•ญ (Prerequisites) ๊ฐœ๋ฐœ ๋ฐ ๋นŒ๋“œ๋ฅผ ์ง„ํ–‰ํ•˜๊ธฐ ์ „์— ๋‹ค์Œ ๋„๊ตฌ๋“ค์ด ์„ค์น˜๋˜์–ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. 1. **Node.js**: v18.0.0 ์ด์ƒ ([๊ณต์‹ ๋‹ค์šด๋กœ๋“œ](https://nodejs.org/)) 2. **Rust**: Latest Stable Toolchain ([rustup ์„ค์น˜ Guide](https://rustup.rs/)) 3. **Tauri v2 OS ๋นŒ๋“œ ํ™˜๊ฒฝ**: - **macOS**: Xcode Command Line Tools (`xcode-select --install`) - **Windows**: C++ Build Tools (Visual Studio Installer) ๋ฐ WebView2 - **Linux**: `webkit2gtk-4.1`, `build-essential`, `libssl-dev` ๋“ฑ --- ## ๐Ÿ’ป ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์‹คํ–‰ (Development) 1. **์ €์žฅ์†Œ ํด๋ก  ๋ฐ ํŒจํ‚ค์ง€ ์„ค์น˜**: ```bash git clone https://gitea.yirugi.synology.me/yirugi/mana-viewer.git cd mana-viewer npm install ``` 2. **๊ฐœ๋ฐœ ๋ชจ๋“œ ๊ตฌ๋™ (Hot Reloading)**: ```bash npm run tauri dev ``` *๊ฐœ๋ฐœ ์„œ๋ฒ„(`vite`)๊ฐ€ 1420 ํฌํŠธ์—์„œ ์‹คํ–‰๋˜๊ณ , Tauri ๋ฐฑ์—”๋“œ๊ฐ€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ˆ˜์ง‘๊ธฐ ๋ฐ ๋ทฐ์–ด ์œˆ๋„์šฐ๋ฅผ ๊ตฌ๋™ํ•ฉ๋‹ˆ๋‹ค.* --- ## ๐Ÿ“ฆ ํ”„๋กœ๋•์…˜ ๋นŒ๋“œ (Production Build) 1. **๋…๋ฆฝ ์‹คํ–‰ํ˜• ์•ฑ ๋นŒ๋“œ**: ```bash npm run tauri build ``` 2. **๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฌผ ์œ„์น˜**: - **macOS ์•ฑ ๋ฒˆ๋“ค (`.app`)**: `src-tauri/target/release/bundle/macos/Mana Viewer.app` - **์‹คํ–‰ ํŒŒ์ผ (Binary)**: `src-tauri/target/release/mana-viewer` (๋˜๋Š” `tauri-app`) *์ƒ์„ฑ๋œ `Mana Viewer.app` ํŒŒ์ผ์„ `/Applications` (์‘์šฉ ํ”„๋กœ๊ทธ๋žจ) ํด๋”๋กœ ์˜ฎ๊ฒจ ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.* --- ## โš™๏ธ ์‚ฌ์ดํŠธ ํ”„๋กœํ•„ (Site Profile) ์ถ”๊ฐ€ ๋ฐ ์„ค์ • Mana Viewer๋Š” **`src-tauri/profiles/`** ๋””๋ ‰ํ† ๋ฆฌ์— ์ €์žฅ๋œ JSON ํŒŒ์ผ๋“ค์„ ์ฝ์–ด ํŒŒ์‹ฑ ๊ทœ์น™์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค. ### ๐Ÿ“„ ํ”„๋กœํ•„ JSON ์˜ˆ์‹œ (`src-tauri/profiles/custom_site.json`) ```json { "id": "custom_site", "name": "์ƒˆ ๋งŒํ™” ์‚ฌ์ดํŠธ", "version": 1, "domains": ["example*.com", "manga*.net"], "selectors": { "images": { "containerSelector": null, "imgSelector": "img", "srcAttributes": ["data-src", "data-original", "data-lazy-src", "src"], "pageOrderAttribute": null, "minWidth": 150, "excludeLandscape": true }, "navigation": { "prevSelectors": [".btn-prev", ".prev"], "nextSelectors": [".btn-next", ".next"], "prevText": "์ด์ „ํ™”", "nextText": "๋‹ค์Œํ™”" }, "title": { "selectors": [".toon-title", "h1.title", "h1"], "fallbackToDocumentTitle": true } }, "filters": { "adClassPattern": "ad|banner|footer|header|popup", "adFilenamePattern": "(^|[\\/_.\\-])(ad|ads|banner|event|popup|logo|icon)([\\/_.\\-]|$)" }, "antiBot": { "spoofViewport": true, "spoofVisibility": true, "fakeWidth": 1920, "fakeHeight": 1080 }, "http": { "needsCookies": true, "refererPolicy": "origin" } } ``` - **์•ฑ ๋‚ด๋ถ€ JSON ์—๋””ํ„ฐ**: ์•ฑ ํ™ˆ ํ™”๋ฉด ํ•˜๋‹จ ๋˜๋Š” ๋ทฐ์–ด ์„ค์ • ๋ฉ”๋‰ด์˜ `๐ŸŒ ์‚ฌ์ดํŠธ ํ…œํ”Œ๋ฆฟ ํ”„๋กœํ•„ ๊ด€๋ฆฌ (JSON)` ๋ฒ„ํŠผ์„ ํ†ตํ•ด ์•ฑ์„ ์ข…๋ฃŒํ•˜์ง€ ์•Š๊ณ  ํ…œํ”Œ๋ฆฟ์„ ์ˆ˜์ • ๋ฐ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. --- ## โŒจ๏ธ ํ‚ค๋ณด๋“œ ๋‹จ์ถ•ํ‚ค (Shortcuts) | ๋‹จ์ถ•ํ‚ค | ๊ธฐ๋Šฅ | | :--- | :--- | | `โ†` / `A` / `KeyS` (ํ•œ๊ธ€ `ใ„ด`) | ์ด์ „ ํŽ˜์ด์ง€ / ์ด์ „ ํ™” ์ด๋™ | | `โ†’` / `D` / `KeyF` (ํ•œ๊ธ€ `ใ„น`) | ๋‹ค์Œ ํŽ˜์ด์ง€ / ๋‹ค์Œ ํ™” ์ด๋™ | | `โ†‘` / `W` | ์ด์ „ ํŽ˜์ด์ง€ ์ด๋™ | | `โ†“` / `S` | ๋‹ค์Œ ํŽ˜์ด์ง€ ์ด๋™ | | `F` | ์ „์ฒดํ™”๋ฉด ํ† ๊ธ€ | | `S` | ์„ค์ • ํŒจ๋„ ์—ด๊ธฐ / ๋‹ซ๊ธฐ | | `Esc` | ์„ค์ • ํŒจ๋„ ๋‹ซ๊ธฐ | --- ## ๐Ÿ“‚ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ (Project Structure) ``` mana-viewer/ โ”œโ”€โ”€ src-tauri/ โ”‚ โ”œโ”€โ”€ profiles/ โ”‚ โ”‚ โ”œโ”€โ”€ _default.json # ๋ฒ”์šฉ ๊ธฐ๋ณธ ํŒŒ์‹ฑ ํ”„๋กœํ•„ (Fallback) โ”‚ โ”‚ โ””โ”€โ”€ newtoki.json # ๋‰ดํ† ๋ผ/๋งˆ๋‚˜ํ† ํ‚ค ์ „์šฉ ํ”„๋กœํ•„ โ”‚ โ””โ”€โ”€ src/ โ”‚ โ”œโ”€โ”€ site_profile.rs # ํ”„๋กœํ•„ ๊ด€๋ฆฌ์ž & ๋™์  INIT_SCRIPT ์ƒ์„ฑ ์—”์ง„ โ”‚ โ”œโ”€โ”€ commands.rs # Tauri IPC ์ปค๋งจ๋“œ & ์ˆ˜์ง‘์šฉ ํ•˜๋ฒ ์Šคํ„ฐ ๋ฃจํ”„ โ”‚ โ””โ”€โ”€ lib.rs # Tauri ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ•ธ๋“ค๋Ÿฌ โ””โ”€โ”€ src/ โ”œโ”€โ”€ utils/ โ”‚ โ””โ”€โ”€ titleParser.ts # ํ™”์ˆ˜/์‹œ์ฆŒ ์ •๋ฐ€ ์ถ”์ถœ ์œ ํ‹ธ๋ฆฌํ‹ฐ โ”œโ”€โ”€ components/ โ”‚ โ”œโ”€โ”€ ProfileManagerModal.tsx # ์ธ์•ฑ JSON ํ”„๋กœํ•„ ์—๋””ํ„ฐ ๋ชจ๋‹ฌ โ”‚ โ”œโ”€โ”€ MangaViewer.tsx # ๋ฉ”์ธ ๋งŒํ™” ๋ทฐ์–ด & ํ† ์ŠคํŠธ ์‹œ์Šคํ…œ โ”‚ โ”œโ”€โ”€ ViewerToolbar.tsx # ์ƒ๋‹จ ์ปจํŠธ๋กค๋ฐ” & ํ™”์ˆ˜ ๋ฐฐ์ง€ โ”‚ โ””โ”€โ”€ UrlInput.tsx # ํ™ˆ ํ™”๋ฉด, URL ์ž…๋ ฅ & ์ตœ๊ทผ ๊ธฐ๋ก ๊ด€๋ฆฌ โ”œโ”€โ”€ hooks/ โ”‚ โ””โ”€โ”€ useChapter.ts # ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์‚ฌ์ „ ์ˆ˜์ง‘ & ์ด๋ฏธ์ง€ ๋ฉ”๋ชจ๋ฆฌ ์บ์‹œ โ””โ”€โ”€ types.ts # SiteProfile ๋ฐ TypeScript ํƒ€์ž… ์ •์˜ ```