# Progressive Web App (PWA) Guide (ํ•œ๊ตญ์–ด) ๐ŸŒ **Languages:** ๐Ÿ‡บ๐Ÿ‡ธ [English](../../../../guides/PWA_GUIDE.md) ยท ๐Ÿ‡ช๐Ÿ‡น [am](../../../am/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ธ๐Ÿ‡ฆ [ar](../../../ar/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฆ๐Ÿ‡ฟ [az](../../../az/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ง๐Ÿ‡ฌ [bg](../../../bg/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ง๐Ÿ‡ฉ [bn](../../../bn/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ง๐Ÿ‡ฆ [bs](../../../bs/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡จ๐Ÿ‡ฟ [cs](../../../cs/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฉ๐Ÿ‡ฐ [da](../../../da/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฉ๐Ÿ‡ช [de](../../../de/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฌ๐Ÿ‡ท [el](../../../el/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ช๐Ÿ‡ธ [es](../../../es/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ช๐Ÿ‡ช [et](../../../et/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ท [fa](../../../fa/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ซ๐Ÿ‡ฎ [fi](../../../fi/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ซ๐Ÿ‡ท [fr](../../../fr/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ช [ga](../../../ga/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [gu](../../../gu/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ณ๐Ÿ‡ฌ [ha](../../../ha/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ฑ [he](../../../he/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [hi](../../../hi/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ญ๐Ÿ‡ท [hr](../../../hr/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ญ๐Ÿ‡บ [hu](../../../hu/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฆ๐Ÿ‡ฒ [hy](../../../hy/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ฉ [id](../../../id/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ณ๐Ÿ‡ฌ [ig](../../../ig/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡น [it](../../../it/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฏ๐Ÿ‡ต [ja](../../../ja/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฌ๐Ÿ‡ช [ka](../../../ka/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฐ๐Ÿ‡ญ [km](../../../km/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [kn](../../../kn/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฑ๐Ÿ‡น [lt](../../../lt/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฑ๐Ÿ‡ป [lv](../../../lv/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [ml](../../../ml/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [mr](../../../mr/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฒ๐Ÿ‡พ [ms](../../../ms/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฒ๐Ÿ‡น [mt](../../../mt/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฒ๐Ÿ‡ฒ [my](../../../my/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ณ๐Ÿ‡ต [ne](../../../ne/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ณ๐Ÿ‡ฑ [nl](../../../nl/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ณ๐Ÿ‡ด [no](../../../no/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [or](../../../or/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [pa](../../../pa/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ต๐Ÿ‡ญ [phi](../../../phi/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ต๐Ÿ‡ฑ [pl](../../../pl/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ต๐Ÿ‡น [pt](../../../pt/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ง๐Ÿ‡ท [pt-BR](../../../pt-BR/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ท๐Ÿ‡ด [ro](../../../ro/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ท๐Ÿ‡บ [ru](../../../ru/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฑ๐Ÿ‡ฐ [si](../../../si/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ธ๐Ÿ‡ฐ [sk](../../../sk/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ธ๐Ÿ‡ฎ [sl](../../../sl/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ท๐Ÿ‡ธ [sr](../../../sr/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ธ๐Ÿ‡ช [sv](../../../sv/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฐ๐Ÿ‡ช [sw](../../../sw/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [ta](../../../ta/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ฎ๐Ÿ‡ณ [te](../../../te/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡น๐Ÿ‡ญ [th](../../../th/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡น๐Ÿ‡ท [tr](../../../tr/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡บ๐Ÿ‡ฆ [uk-UA](../../../uk-UA/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ต๐Ÿ‡ฐ [ur](../../../ur/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡บ๐Ÿ‡ฟ [uz](../../../uz/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ป๐Ÿ‡ณ [vi](../../../vi/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡ณ๐Ÿ‡ฌ [yo](../../../yo/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡จ๐Ÿ‡ณ [zh-CN](../../../zh-CN/docs/guides/PWA_GUIDE.md) ยท ๐Ÿ‡น๐Ÿ‡ผ [zh-TW](../../../zh-TW/docs/guides/PWA_GUIDE.md) --- OmniRoute๋Š” ์™„์ „ํžˆ ์„ค์น˜ ๊ฐ€๋Šฅํ•œ ํ”„๋กœ๊ทธ๋ ˆ์‹œ๋ธŒ ์›น ์•ฑ์œผ๋กœ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค. Android(Chrome) ๋˜๋Š” iOS(Safari) ๋“ฑ ๋ชจ๋ฐ”์ผ ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋Œ€์‹œ๋ณด๋“œ์— ์ ‘์†ํ•˜๋ฉด ์•ฑ ์Šคํ† ์–ด ์—†์ด๋„ "ํ™ˆ ํ™”๋ฉด์— ์ถ”๊ฐ€"ํ•˜์—ฌ ๋„ค์ดํ‹ฐ๋ธŒ ์•ฑ๊ณผ ๊ฐ™์€ ํ™˜๊ฒฝ์„ ์ด์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ## PWA๋ž€ ๋ฌด์—‡์ธ๊ฐ€์š”? ํ”„๋กœ๊ทธ๋ ˆ์‹œ๋ธŒ ์›น ์•ฑ์€ OmniRoute ์›น ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ๋„ค์ดํ‹ฐ๋ธŒ ๋ชจ๋ฐ”์ผ ์•ฑ์ฒ˜๋Ÿผ ๋ณด์ด๊ณ  ์ž‘๋™ํ•˜๋„๋ก ๋งŒ๋“ค์–ด ์ค๋‹ˆ๋‹ค. ์„ค์น˜ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. - ์ž์ฒด ์•„์ด์ฝ˜์„ ์‚ฌ์šฉํ•˜์—ฌ ํ™ˆ ํ™”๋ฉด์—์„œ ์‹คํ–‰ - ๋ธŒ๋ผ์šฐ์ € ์ฃผ์†Œ ํ‘œ์‹œ์ค„์ด๋‚˜ ํƒญ UI ์—†์ด ์ „์ฒด ํ™”๋ฉด์œผ๋กœ ์‹คํ–‰ - ์ „์šฉ ์—ฐ๊ฒฐ ์ƒํƒœ ํŽ˜์ด์ง€๋ฅผ ํ†ตํ•ด ์˜คํ”„๋ผ์ธ์—์„œ๋„ ์ž‘๋™ - ๋” ๋น ๋ฅธ ๋กœ๋”ฉ์„ ์œ„ํ•ด ์ •์  ์ž์‚ฐ ์บ์‹ฑ - ์„ธ๋กœ ๋ฐ ๊ฐ€๋กœ ํ™”๋ฉด ๋ฐฉํ–ฅ ๋ชจ๋‘ ์ง€์› ## ์„ค์น˜ ### Android (Chrome) 1. Chrome์—์„œ OmniRoute ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ์—ฝ๋‹ˆ๋‹ค: `http://YOUR_IP:20128` 2. Chrome์— **"OmniRoute๋ฅผ ํ™ˆ ํ™”๋ฉด์— ์ถ”๊ฐ€"** ๋ฐฐ๋„ˆ๊ฐ€ ์ž๋™์œผ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ๋˜๋Š” ๋‹ค์Œ ๋‹จ๊ณ„๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค. - **โ‹ฎ** ๋ฉ”๋‰ด(์  3๊ฐœ) โ†’ **"ํ™ˆ ํ™”๋ฉด์— ์ถ”๊ฐ€"** ๋˜๋Š” **"์•ฑ ์„ค์น˜"**๋ฅผ ํƒญํ•ฉ๋‹ˆ๋‹ค. 3. ๋ฉ”์‹œ์ง€๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. 4. OmniRoute๊ฐ€ ํ™ˆ ํ™”๋ฉด์— ๋…๋ฆฝ ์‹คํ–‰ํ˜• ์•ฑ์œผ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ### iOS (Safari) 1. Safari์—์„œ OmniRoute ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ์—ฝ๋‹ˆ๋‹ค: `http://YOUR_IP:20128` 2. **๊ณต์œ ** ๋ฒ„ํŠผ(ํ™”์‚ดํ‘œ๊ฐ€ ์žˆ๋Š” ์ƒ์ž)์„ ํƒญํ•ฉ๋‹ˆ๋‹ค. 3. ์•„๋ž˜๋กœ ์Šคํฌ๋กคํ•˜์—ฌ **"ํ™ˆ ํ™”๋ฉด์— ์ถ”๊ฐ€"**๋ฅผ ํƒญํ•ฉ๋‹ˆ๋‹ค. 4. ์ด๋ฆ„์„ ์ง€์ •ํ•˜๊ณ (๊ธฐ๋ณธ๊ฐ’: "OmniRoute") **์ถ”๊ฐ€**๋ฅผ ํƒญํ•ฉ๋‹ˆ๋‹ค. 5. OmniRoute๊ฐ€ ์•ฑ ์•„์ด์ฝ˜๊ณผ ํ•จ๊ป˜ ํ™ˆ ํ™”๋ฉด์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ### ๋ฐ์Šคํฌํ†ฑ (Chrome / Edge) 1. OmniRoute ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ์—ฝ๋‹ˆ๋‹ค. 2. ์ฃผ์†Œ ํ‘œ์‹œ์ค„์—์„œ **์„ค์น˜ ์•„์ด์ฝ˜**์„ ํด๋ฆญํ•˜๊ฑฐ๋‚˜ โ‹ฎ โ†’ "OmniRoute ์„ค์น˜..."๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. 3. ๋ฉ”์‹œ์ง€๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. 4. OmniRoute๊ฐ€ ํƒญ์ด๋‚˜ ์ฃผ์†Œ ํ‘œ์‹œ์ค„์ด ์—†๋Š” ๋…๋ฆฝ ์‹คํ–‰ํ˜• ์ฐฝ์œผ๋กœ ์—ด๋ฆฝ๋‹ˆ๋‹ค. ## ๊ธฐ๋Šฅ ### ์ „์ฒด ํ™”๋ฉด ํ™˜๊ฒฝ ๋งค๋‹ˆํŽ˜์ŠคํŠธ๋Š” `display: "fullscreen"`์œผ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ์„ค์น˜๋œ ์•ฑ์ด ๋ธŒ๋ผ์šฐ์ € UI๋‚˜ ์ƒํƒœ ํ‘œ์‹œ์ค„ ๊ฒน์นจ ์—†์ด ์ „์ฒด ํ™”๋ฉด์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋Œ€์‹œ๋ณด๋“œ๊ฐ€ ์ง„์ •ํ•œ ๋„ค์ดํ‹ฐ๋ธŒ ์•ฑ์ฒ˜๋Ÿผ ๋А๊ปด์ง‘๋‹ˆ๋‹ค. ### ์˜คํ”„๋ผ์ธ ์ง€์› OmniRoute์—๋Š” ์ง€๋Šฅํ˜• ์บ์‹ฑ์„ ์ œ๊ณตํ•˜๋Š” ์„œ๋น„์Šค ์›Œ์ปค(`sw.js`)๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. | ์ž์‚ฐ ์œ ํ˜• | ์ „๋žต | ๋™์ž‘ | | ------------------------------------------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------- | | **์•ฑ ์…ธ** | ์บ์‹œ ์šฐ์„  | ์„ค์น˜ ์‹œ `/`, `/offline`, ๋งค๋‹ˆํŽ˜์ŠคํŠธ ๋ฐ ์•„์ด์ฝ˜์„ ๋ฏธ๋ฆฌ ์บ์‹ฑ | | **์ •์  ์ž์‚ฐ** (CSS, JS, ์ด๋ฏธ์ง€, ๊ธ€๊ผด) | ๋„คํŠธ์›Œํฌ ์šฐ์„  ๋ฐ ์บ์‹œ ํด๋ฐฑ | ๋„คํŠธ์›Œํฌ์—์„œ ์ตœ์‹  ํ•ญ๋ชฉ์„ ๊ฐ€์ ธ์˜ค๋ฉฐ, ์˜คํ”„๋ผ์ธ์ด๋ฉด ์บ์‹œ๋กœ ํด๋ฐฑ | | **Next.js ๋ฒˆ๋“ค** (`/_next/`) | ๋„คํŠธ์›Œํฌ ์šฐ์„  ๋ฐ ์บ์‹œ ์—…๋ฐ์ดํŠธ | ๋„คํŠธ์›Œํฌ์—์„œ ๊ฐ€์ ธ์™€ ์บ์‹œ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋ฉฐ, ์˜คํ”„๋ผ์ธ์ด๋ฉด ์บ์‹œ๋œ ๋ฒ„์ „์„ ์ œ๊ณต | | **ํƒ์ƒ‰ ์š”์ฒญ** | ์šฐํšŒ(์ ˆ๋Œ€ ๊ฐ€๋กœ์ฑ„์ง€ ์•Š์Œ) | ๋ธŒ๋ผ์šฐ์ €๊ฐ€ HTTP/3โ†’HTTP/2 ํด๋ฐฑ์„ ์ฒ˜๋ฆฌํ•˜๋ฉฐ, ์‘๋‹ตํ•˜์ง€ ์•Š๋Š” QUIC ์†Œ์ผ“์ด `Response.error()`๊ฐ€ ๋˜์–ด์„œ๋Š” ์•ˆ ๋จ | | **API / ๋Œ€์‹œ๋ณด๋“œ ๊ฒฝ๋กœ** (`/api/`, `/a2a`, `/dashboard`) | ์šฐํšŒ(์ ˆ๋Œ€ ์บ์‹ฑํ•˜์ง€ ์•Š์Œ) | ํ•ญ์ƒ ์„œ๋ฒ„๋กœ ์ง์ ‘ ์ „๋‹ฌ๋˜๋ฉฐ ์„œ๋น„์Šค ์›Œ์ปค๊ฐ€ ์ ˆ๋Œ€ ๊ฐ€๋กœ์ฑ„์ง€ ์•Š์Œ | ### ์˜คํ”„๋ผ์ธ ํŽ˜์ด์ง€ ๋„คํŠธ์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ์ƒํƒœ์—์„œ ์‚ฌ์šฉ์ž๊ฐ€ ์ƒˆ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜๋ฉด ์„œ๋น„์Šค ์›Œ์ปค๊ฐ€ ์ „์šฉ `/offline` ํŽ˜์ด์ง€๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด ํŽ˜์ด์ง€๋Š” ๋‹ค์Œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. - ๋ช…ํ™•ํ•œ **"์—ฐ๊ฒฐ ๋ฌธ์ œ"** ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ - ์‹ค์‹œ๊ฐ„์œผ๋กœ ์—…๋ฐ์ดํŠธ๋˜๋Š” **์˜จ๋ผ์ธ/์˜คํ”„๋ผ์ธ ์ƒํƒœ ํ‘œ์‹œ๊ธฐ** ์ œ๊ณต - ์—ฐ๊ฒฐ์ด ๋ณต๊ตฌ๋˜์—ˆ์„ ๋•Œ ๋‹ค์‹œ ๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋Š” **"์—ฐ๊ฒฐ ์žฌ์‹œ๋„"** ๋ฒ„ํŠผ ์ œ๊ณต - ์ง„๋‹จ์„ ์œ„ํ•œ **์ƒํƒœ ํŽ˜์ด์ง€** ๋งํฌ ์ œ๊ณต ### ์•ฑ ์•„์ด์ฝ˜ OmniRoute๋Š” ๊ฐ ํ”Œ๋žซํผ์— ์ตœ์ ํ™”๋œ ์•„์ด์ฝ˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. | ํŒŒ์ผ | ํฌ๊ธฐ | ์‚ฌ์šฉ ์œ„์น˜ | | ---------------------- | ------------- | ------------------------------ | | `icon-512.png` | 512ร—512 | Android ์„ค์น˜ ๋ฉ”์‹œ์ง€, ์‹œ์ž‘ ํ™”๋ฉด | | `apple-touch-icon.png` | 180ร—180 | iOS ํ™ˆ ํ™”๋ฉด ์•„์ด์ฝ˜ | | `icon-192.svg` | 192ร—192(๋ฒกํ„ฐ) | Android ์ ์‘ํ˜• ์•„์ด์ฝ˜ | | `apple-touch-icon.svg` | 180ร—180(๋ฒกํ„ฐ) | Apple ํด๋ฐฑ | | `favicon.svg` | ๋ฒกํ„ฐ | ๋ธŒ๋ผ์šฐ์ € ํƒญ | | `favicon.ico` | ๋‹ค์ค‘ ํฌ๊ธฐ | ๋ ˆ๊ฑฐ์‹œ ๋ธŒ๋ผ์šฐ์ € | ### ์ž๋™ ๋“ฑ๋ก ์„œ๋น„์Šค ์›Œ์ปค๋Š” ๋ฃจํŠธ ๋ ˆ์ด์•„์›ƒ์˜ `` ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ†ตํ•ด ์ž๋™์œผ๋กœ ๋“ฑ๋ก๋ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ๋ณ„๋„๋กœ ์กฐ์น˜ํ•  ํ•„์š”๊ฐ€ ์—†์œผ๋ฉฐ, ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์œ ํšจํ•œ ๋งค๋‹ˆํŽ˜์ŠคํŠธ์™€ ์„œ๋น„์Šค ์›Œ์ปค๋ฅผ ๊ฐ์ง€ํ•˜๋Š” ์ฆ‰์‹œ ์•ฑ์„ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ## ๊ธฐ์ˆ  ์•„ํ‚คํ…์ฒ˜ ### ์›น ์•ฑ ๋งค๋‹ˆํŽ˜์ŠคํŠธ (`manifest.webmanifest`) Next.js๊ฐ€ `src/app/manifest.ts`๋ฅผ ํ†ตํ•ด ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ```json { "name": "OmniRoute", "short_name": "OmniRoute", "description": "OmniRoute is an AI gateway for multi-provider LLMs. One endpoint for all your AI providers.", "start_url": "/", "scope": "/", "display": "fullscreen", "orientation": "any", "background_color": "#0b0f1a", "theme_color": "#0b0f1a", "icons": [ { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" }, { "src": "/apple-touch-icon.png", "sizes": "180x180", "type": "image/png" } ] } ``` ### ์„œ๋น„์Šค ์›Œ์ปค (`public/sw.js`) ํ”„๋ ˆ์ž„์›Œํฌ ์ข…์†์„ฑ์ด ์—†๋Š” ์ˆœ์ˆ˜ ์„œ๋น„์Šค ์›Œ์ปค์ด๋ฉฐ, ๋‹ค์Œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. - **์„ค์น˜ ๋‹จ๊ณ„**: ์•ฑ ์…ธ(๋ฃจํŠธ, ์˜คํ”„๋ผ์ธ ํŽ˜์ด์ง€, ๋งค๋‹ˆํŽ˜์ŠคํŠธ, ์•„์ด์ฝ˜)์„ ๋ฏธ๋ฆฌ ์บ์‹œํ•ฉ๋‹ˆ๋‹ค - **ํ™œ์„ฑํ™” ๋‹จ๊ณ„**: ์ด์ „ ์บ์‹œ ๋ฒ„์ „์„ ์ •๋ฆฌํ•˜๊ณ  ๋ชจ๋“  ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค - **๊ฐ€์ ธ์˜ค๊ธฐ ๋‹จ๊ณ„**: ์š”์ฒญ ์œ ํ˜•(ํƒ์ƒ‰, ์ •์  ์ž์‚ฐ, API)์— ๋”ฐ๋ผ ์ง€๋Šฅ์ ์œผ๋กœ ๋ผ์šฐํŒ…ํ•ฉ๋‹ˆ๋‹ค - **์บ์‹œ ๋ฒ„์ „ ๊ด€๋ฆฌ**: `omniroute-pwa-v3` โ€” ์—…๋ฐ์ดํŠธ ์‹œ ์ƒˆ ์บ์‹œ๋ฅผ ๊ฐ•์ œ๋กœ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์ด ๊ฐ’์„ ์˜ฌ๋ฆฌ์„ธ์š” ### ๋ ˆ์ด์•„์›ƒ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ (`src/app/layout.tsx`) ๋ฃจํŠธ ๋ ˆ์ด์•„์›ƒ์€ PWA ์š”๊ตฌ ์‚ฌํ•ญ์„ ์ค€์ˆ˜ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ๋ชจ๋“  ๋ฉ”ํƒ€ ํƒœ๊ทธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. - `/manifest.webmanifest`๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” `manifest` ๋งํฌ - iOS ๋…๋ฆฝ ์‹คํ–‰ํ˜• ๋ชจ๋“œ๋ฅผ ์œ„ํ•œ `apple-web-app-capable: true` - `apple-web-app-status-bar-style: black-translucent` - Android Chrome์„ ์œ„ํ•œ `mobile-web-app-capable: yes` - `theme-color: #0b0f1a` - ํ™”๋ฉด ๊ฐ€์žฅ์ž๋ฆฌ๊นŒ์ง€ ๋ Œ๋”๋งํ•˜๊ธฐ ์œ„ํ•œ `viewport-fit: cover` ### ์ปดํฌ๋„ŒํŠธ: `PwaRegister` `src/shared/components/PwaRegister.tsx`์— ์žˆ๋Š” ์ด ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. 1. ๋งˆ์šดํŠธ ์‹œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค(ํด๋ผ์ด์–ธํŠธ ์ธก์—์„œ๋งŒ) 2. ๋ธŒ๋ผ์šฐ์ €์˜ `serviceWorker` ์ง€์› ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค 3. `/sw.js`๋ฅผ ์กฐ์šฉํžˆ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค(์•ฑ ์‹คํ–‰์„ ๋ฐฉํ•ดํ•˜์ง€ ์•Š๋„๋ก ์˜ค๋ฅ˜๋Š” ๋ฌด์‹œ๋ฉ๋‹ˆ๋‹ค) 4. ์•„๋ฌด๊ฒƒ๋„ ๋ Œ๋”๋งํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค(`return null`) โ€” ๋ถ€์ˆ˜ ํšจ๊ณผ๋งŒ ์ˆ˜ํ–‰ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์ž…๋‹ˆ๋‹ค ## Termux(Android)์—์„œ ์‚ฌ์šฉ Termux๋ฅผ ํ†ตํ•ด Android์—์„œ OmniRoute๋ฅผ ์‹คํ–‰ํ•˜๋ฉด PWA๊ฐ€ ์›ํ™œํ•˜๊ฒŒ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. 1. Termux์—์„œ OmniRoute๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค: `npx omniroute` 2. ๊ฐ™์€ ํœด๋Œ€์ „ํ™”์—์„œ Chrome์„ ์—ฝ๋‹ˆ๋‹ค: `http://localhost:20128` 3. "ํ™ˆ ํ™”๋ฉด์— ์ถ”๊ฐ€"๋ฅผ ํ†ตํ•ด PWA๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค 4. PWA๊ฐ€ ๋กœ์ปฌ Termux ์„œ๋ฒ„์— ์—ฐ๊ฒฐ๋˜๋ฉฐ, ๋ชจ๋“  ๊ฒƒ์ด ๊ธฐ๊ธฐ ๋‚ด์—์„œ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค ์ด ์กฐํ•ฉ์„ ์‚ฌ์šฉํ•˜๋ฉด Android ํœด๋Œ€์ „ํ™”๊ฐ€ **์„œ๋ฒ„**(Termux)์ด์ž **ํด๋ผ์ด์–ธํŠธ**(PWA)๊ฐ€ ๋˜์–ด ์™„์ „ํ•œ ๋…๋ฆฝํ˜• AI ๊ฒŒ์ดํŠธ์›จ์ด๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ## ๋‹ค๋ฅธ ๊ธฐ๊ธฐ์—์„œ ์‚ฌ์šฉ OmniRoute ์„œ๋ฒ„์— ๋ธŒ๋ผ์šฐ์ €๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ๊ธฐ๊ธฐ์— PWA๋ฅผ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. - **๋‹ค๋ฅธ ํœด๋Œ€์ „ํ™”/ํƒœ๋ธ”๋ฆฟ**: `http://PHONE_IP:20128`๋กœ ์ด๋™ํ•˜์—ฌ PWA๋ฅผ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค - **๋…ธํŠธ๋ถ**: Chrome/Edge์—์„œ ์—ด๊ณ  ๋ฐ์Šคํฌํ†ฑ PWA๋กœ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค - **๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์žˆ๋Š” ์Šค๋งˆํŠธ TV**: ๋Œ€์‹œ๋ณด๋“œ๋ฅผ ์ „์ฒด ํ™”๋ฉด์œผ๋กœ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค ## ์‚ฌ์šฉ์ž ์ง€์ • ### ์ธ์Šคํ„ด์Šค ์ด๋ฆ„ PWA ์ œ๋ชฉ์—๋Š” `Dashboard โ†’ Settings`์˜ **Instance Name** ์„ค์ •์ด ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค. ์ธ์Šคํ„ด์Šค ์ด๋ฆ„์„ "My AI Gateway"๋กœ ๋ณ€๊ฒฝํ•˜๋ฉด ์„ค์น˜๋œ PWA์— ํ•ด๋‹น ์ด๋ฆ„์ด ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค. ### ์‚ฌ์šฉ์ž ์ง€์ • ํŒŒ๋น„์ฝ˜ `Dashboard โ†’ Settings`์—์„œ ์‚ฌ์šฉ์ž ์ง€์ • ํŒŒ๋น„์ฝ˜์„ ์—…๋กœ๋“œํ•˜๋ฉด ๋ฐ์Šคํฌํ†ฑ์˜ PWA ์•„์ด์ฝ˜์— ํ•ด๋‹น ์‚ฌ์šฉ์ž ์ง€์ • ์•„์ด์ฝ˜์ด ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค. ๋ชจ๋ฐ”์ผ ํ™ˆ ํ™”๋ฉด ์•„์ด์ฝ˜์—๋Š” ๋ฏธ๋ฆฌ ๋นŒ๋“œ๋œ `icon-512.png` ๋ฐ `apple-touch-icon.png` ํŒŒ์ผ์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ## ์ œํ•œ ์‚ฌํ•ญ - **ํ‘ธ์‹œ ์•Œ๋ฆผ ๋ฏธ์ง€์›** โ€” ์„œ๋น„์Šค ์›Œ์ปค๋Š” Push API๋ฅผ ๊ตฌํ˜„ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  Electron ์•ฑ์—์„œ ์•Œ๋ฆผ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. - **๋ฐฑ๊ทธ๋ผ์šด๋“œ ๋™๊ธฐํ™” ๋ฏธ์ง€์›** โ€” ์˜คํ”„๋ผ์ธ ์ž‘์—…์€ ๋‚˜์ค‘์— ์žฌ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋Œ€๊ธฐ์—ด์— ์ถ”๊ฐ€๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. PWA๋Š” ์ฃผ๋กœ ๋Œ€์‹œ๋ณด๋“œ ๋ทฐ์–ด๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. - **iOS ์ œํ•œ ์‚ฌํ•ญ** โ€” iOS์˜ Safari๋Š” ๋ชจ๋“  PWA ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค(์˜ˆ: ์„ค์น˜ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์‹คํ–‰ํ•ด์•ผ ํ•˜๋ฉฐ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค ์›Œ์ปค ๊ธฐ๋Šฅ์ด ์ œํ•œ๋ฉ๋‹ˆ๋‹ค). - **์บ์‹œ ํฌ๊ธฐ** โ€” ์„œ๋น„์Šค ์›Œ์ปค๋Š” ์ •์  ์ž์‚ฐ๋งŒ ์บ์‹œํ•ฉ๋‹ˆ๋‹ค. `/api/` ๊ฒฝ๋กœ์˜ ๋Œ€์šฉ๋Ÿ‰ ์‘๋‹ต ํŽ˜์ด๋กœ๋“œ๋Š” ์บ์‹œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. - **๋ชจ๋ฐ”์ผ์˜ ์‚ฌ์šฉ์ž ์ง€์ • ์•„์ด์ฝ˜** โ€” ์„ค์ •์—์„œ ํŒŒ๋น„์ฝ˜์„ ๋ณ€๊ฒฝํ•ด๋„ ๋ชจ๋ฐ”์ผ์˜ ํ™ˆ ํ™”๋ฉด ์•„์ด์ฝ˜์€ ์—…๋ฐ์ดํŠธ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค(์ด๋ฅผ ์œ„ํ•ด์„œ๋Š” PWA ์•„์ด์ฝ˜์„ ๋‹ค์‹œ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค). ## ํŒŒ์ผ ์ฐธ์กฐ | ํŒŒ์ผ | ์šฉ๋„ | | --------------------------------------- | -------------------------------------------------------------------- | | `src/app/manifest.ts` | Next.js ๋งค๋‹ˆํŽ˜์ŠคํŠธ ๊ฒฝ๋กœ(`manifest.webmanifest` ์ƒ์„ฑ) | | `public/sw.js` | ์บ์‹ฑ ๋กœ์ง์ด ํฌํ•จ๋œ ์„œ๋น„์Šค ์›Œ์ปค | | `src/shared/components/PwaRegister.tsx` | ์„œ๋น„์Šค ์›Œ์ปค๋ฅผ ๋“ฑ๋กํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ | | `src/app/offline/page.tsx` | ์‹ค์‹œ๊ฐ„ ์ƒํƒœ ํ‘œ์‹œ๊ธฐ๊ฐ€ ํฌํ•จ๋œ ์˜คํ”„๋ผ์ธ ๋Œ€์ฒด ํŽ˜์ด์ง€ | | `src/app/layout.tsx` | PWA ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๊ฐ€ ํฌํ•จ๋œ ๋ฃจํŠธ ๋ ˆ์ด์•„์›ƒ(apple-web-app, theme-color ๋“ฑ) | | `public/icon-512.png` | 512ร—512 PNG ์•„์ด์ฝ˜(Android, ์Šคํ”Œ๋ž˜์‹œ ํ™”๋ฉด) | | `public/apple-touch-icon.png` | 180ร—180 PNG ์•„์ด์ฝ˜(iOS ํ™ˆ ํ™”๋ฉด) | | `public/icon-192.svg` | 192ร—192 SVG ์•„์ด์ฝ˜(Android ์ ์‘ํ˜•) | | `public/apple-touch-icon.svg` | 180ร—180 SVG ์•„์ด์ฝ˜(Apple ๋Œ€์ฒด์šฉ) |