๊ฐ์
์น ์๋น์ค๋ฅผ ์ฝ๋ ๊ณต๊ฐ ์์ด ์ธ๋ถ์์ ์คํํ ์ ์๋๋ก exe ํ์ผ๋ก ๋ณํํ๋ ค ํจ
ํ์ธ
Fastapi ์๋ฒ๋ python์ผ๋ก ์์ฑ๋์ด ์์ผ๋ฏ๋ก pyinstaller ๋ฅผ ์ฌ์ฉํ์ฌ ์คํ ํ์ผ์ ํจํค์งํ๋ ค ํจ
์งํ
1. main.py ์์ if __name__ == "__main__": ๋ธ๋ก์ผ๋ก ์คํ๋๋ uvicorn ๋ถ๋ฆฌํ๊ธฐ
- ์ด์
- pyinstaller๋ application์ ํจํค์งํ ๋ model import ์์์ ์ฐธ์กฐ ๋ฐฉ์์ ์ํฅ์ ๋ฐ์ ์ ์๋ค.
- if __name__ == "__main__": ๋ธ๋ก์ ์ ์์ ์ผ๋ก ์ธ์ํ์ง ๋ชปํด์ ASGI ์๋ฒ(์ฆ, uvicorn)๋ฅผ ์คํํ์ง ๋ชปํ๋ค.
- ์งํ
- main.py ์์๋ ๋จ์ํ Fastapi ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ํ๋ ์ญํ ๋ง ํ๊ณ , run_server.py์์ main.py๋ฅผ module๋ก importํ์ฌ app์ ์คํํ๋ค.
๋ณ๊ฒฝ ์
main.py
import uvicorn
from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.middleware.cors import CORSMiddleware
from api.api import api_router
app = FastAPI(title="fastapi_app", openapi_url="/api/v1/openapi.json")
app.include_router(api_router)
app.mount("/static", StaticFiles(directory="static"), name="static")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
templates = Jinja2Templates(directory="templates")
@app.get("/", tags=["root"], response_class=HTMLResponse)
async def read_root(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
if __name__ == "__main__":
uvicorn.run("main:app", host='0.0.0.0', port=8000)
๋ณ๊ฒฝ ํ
main.py
from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.middleware.cors import CORSMiddleware
from api.api import api_router
app = FastAPI(title="fastapi_app", openapi_url="/api/v1/openapi.json")
app.include_router(api_router)
app.mount("/static", StaticFiles(directory="static"), name="static")
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
templates = Jinja2Templates(directory="templates")
@app.get("/", tags=["root"], response_class=HTMLResponse)
async def read_root(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
run_server.py
from main import app
if __name__ == "__main__":
import asyncio
from hypercorn.config import Config
from hypercorn.asyncio import serve
config = Config()
config.bind = ["0.0.0.0:8000"]
asyncio.run(serve(app, config))
โ uvicorn.run()์ ์ฌ์ฉํ์ง ์๊ณ asyncio.run() ์ผ๋ก ์คํ ์์ผฐ๋ค.
→ uvicorn.run() ๋ ์์ฒด์ ์ผ๋ก ์ด๋ฒคํธ ๋ฃจํ๋ฅผ ์์ฑํ๊ณ ๊ด๋ฆฌํ๋ค. ๊ทธ๋ฌ๋ ์ด์๋ ๋ณ๊ฐ๋ก pyinstaller ๋ก ํจํค์งํ ๋ asyncio ์ ์ด๋ฒคํธ ๋ฃจํ๊ฐ ์ค๋ณต๋์ด ๋ฐ์ํ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค. (ํนํ, windows์์๋ ์ด๋ฌํ ์ถฉ๋์ด ๋ฐ์ํ ๊ฐ๋ฅ์ฑ์ด ๋๋ค.)
→ uvicorn.run()๋ ๋น๋๊ธฐ ํ๊ฒฝ์์ ๋๊ธฐ์ ์ผ๋ก ํธ์ถ๋๋ฏ๋ก, ๋ด๋ถ์ ์ผ๋ก ์ด๋ฒคํธ ๋ฃจํ๋ฅผ ์์ํ๊ณ ์ด๋ฅผ ๋๊ธฐ ๋ฐฉ์์ผ๋ก ์คํ์ํค๋ ค๊ณ ํ๋ค. ํ์ง๋ง asyncio.run()์ ์ด๋ฒคํธ ๋ฃจํ๋ฅผ ์ง์ ์ ์ดํ๊ธฐ ๋๋ฌธ์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ด๋ฒคํธ ๋ฃจํ๊ฐ ์๋ ํ๊ฒฝ์์ ์ฌ์ฉํ๋๋ก ์ค๊ณ๋์ด ์๋ค. asyncio.run์ ํตํด ๋ช ์์ ์ผ๋ก ์ด๋ฒคํธ ๋ฃจํ๋ฅผ ์์ฑํ์ฌ ๋น๋๊ธฐ ์๋ฒ๋ฅผ ์คํํ๋ฉด, ์ถฉ๋ ์์ด ๋ ์์ ์ ์ผ๋ก ์๋ํ ์๋ค!
2. ์คํ ํ์ผ์์ static , templates ํด๋ ๊ฒฝ๋ก ์ค์
HTML ํ ํ๋ฆฟ, CSS, JavaScript์ ๊ฐ์ ์ ์ ๋ฆฌ์์ค๋ฅผ ํฌํจํ๋ static๊ณผ templates ํด๋ FastAPI๊ฐ ์ด ํด๋๋ค์ ํน์ ๊ฒฝ๋ก์์ ์ฐพ๋๋ค. PyInstaller๋ก ํจํค์งํ ๋๋ ์คํ ํ์ผ์ด ์์ ๋๋ ํ ๋ฆฌ์์ ์คํ๋๋ฏ๋ก, static ๋ฐ templates ํด๋์ ์์น๋ฅผ ๋์ ์ผ๋ก ์ค์ ํด ์ค์ผ ํ๋ค.
๋ณ๊ฒฝ ์
# static
app.mount("/static", StaticFiles(directory="static"), name="static")
# templates
templates = Jinja2Templates(directory="templates")
๋ณ๊ฒฝ ํ
# PyInstaller ํ๊ฒฝ์์ ์คํ ์ค์ธ ๊ฒฝ์ฐ์ ๊ฒฝ๋ก ์ค์
if getattr(sys, 'frozen', False):
base_dir = sys._MEIPASS # PyInstaller๊ฐ ์ค์ ํ๋ ์์ ํด๋ ๊ฒฝ๋ก
else:
base_dir = os.path.dirname(os.path.abspath(__file__))
# static
app.mount("/static", StaticFiles(directory=os.path.join(base_dir, "static")), name="static")
# templates
templates = Jinja2Templates(directory=os.path.join(base_dir, "templates"))
โ ์ถ๊ฐ๋ก ์ฐธ์กฐํ๋ ํ์ผ์ด ์์ผ๋ฉด ๋์ผํ๊ฒ ๊ฒฝ๋ก๋ฅผ ์ถ๊ฐํด์ค๋ค.
→ json ํ์ผ์ ์ฐธ์กฐํ๋ ๋ถ๋ถ์ด ์์ด์ ๋์ผํ๊ฒ ๊ฒฝ๋ก๋ฅผ ์ถ๊ฐํด์ฃผ์๋ค
if getattr(sys, 'frozen', False):
base_dir = sys._MEIPASS
else:
base_dir = os.path.dirname(os.path.abspath(__file__))
json_file_path = os.path.join(base_dir, "projects.json")
3. pyinstaller ์คํ
1. pyinstaller ์ค์นํด์ค๋ค.
pip3 install pyinstaller
2. app์ ์คํ์ํค๋ run_server.py ์ ๋์ผํ ์์น์์ ์๋์ ๋ช ๋ น์ด๋ฅผ ์คํํ๋ค.
- MacOS : --add-data ๋ถ๋ถ์ ":" ์ฌ์ฉ
pyinstaller --onefile --name fastapi_app --add-data "static:static" --add-data "templates:templates" --add-data "projects.json:." run_server.py
- Windows : --add-data ๋ถ๋ถ์ ";" ์ฌ์ฉ
pyinstaller --onefile --name fastapi_app --add-data "static;static" --add-data "templates;templates" --add-data "projects.json;." run_server.py
์ฑ๊ณต์ ์ผ๋ก ์คํ์ด ์๋ฃ๋๋ฉด 2๊ฐ์ ํด๋(build, dist), 1๊ฐ์ spec ํ์ผ(fastapi_app.spec)์ด ์์ฑ๋๋ค.
- build
- build ํด๋๋ ํจํค์ง ๊ณผ์ ์์ ๋ฐ์ํ๋ ์์ ํ์ผ์ ์ ์ฅํ๋ค.
- ํจํค์ง์ด ๋๋ ํ์๋ build ํด๋๊ฐ ํ์ ์์ผ๋ฏ๋ก ์ญ์ ํด๋ ๋ฌด๋ฐฉํ๋ค.
- dist
- dist ํด๋๋ ์ต์ข ํจํค์ง๋ ์คํ ํ์ผ์ ์ ์ฅํ๋ ์์น
- --onefile ์ต์ ์ ์ฌ์ฉํ์ง ์์ ๊ฒฝ์ฐ: dist ํด๋์ ๋ฉ์ธ ์คํ ํ์ผ๊ณผ ํจ๊ป ๋ชจ๋ ์์กด์ฑ์ด ํฌํจ๋ ํ์ ํด๋๊ฐ ์์ฑ๋๋ค.
- --onefile ์ต์ ์ ์ฌ์ฉํ ๊ฒฝ์ฐ: dist ํด๋์ ๋จ์ผ .exe ํ์ผ์ด ์์ฑ๋๋ค.
- ์ด ํด๋๋ ์ต์ข ๊ฒฐ๊ณผ๋ฌผ์ด๋ฏ๋ก, exe ํ์ผ์ ๋ค๋ฅธ ์์น์ ๋ณต์ฌํ์ฌ ์ฌ์ฉํ๋ฉด ๋๋ค.
- .spec ํ์ผ
- .spec ํ์ผ์ PyInstaller์ ์ค์ ํ์ผ๋ก, ํจํค์ง ๊ณผ์ ์์ PyInstaller๊ฐ ์ฌ์ฉํ ์ค์ ์ ํฌํจํ๋ค.
- --add-data ์ต์ ์ผ๋ก ์ถ๊ฐํ๋ ํ์ผ๋ค, --hidden-import์ผ๋ก ์จ๊ฒจ์ง ์ํฌํธ๋ฅผ ์ถ๊ฐํ๋ ์ ๋ณด ๋ฑ์ด ์ด ํ์ผ์ ์ ์ฅ๋๋ค.
- .spec ํ์ผ์ ์์ ํ์ฌ ํจํค์ง ์ธ๋ถ ์ค์ ์ ๋ณ๊ฒฝํ๊ณ , ์ด ํ์ผ์ ์ฌ์ฉํด ๋ค์ ๋น๋ํ ์ ์๋ค.
pyinstaller fastapi_app.spec
4. ์์ฑ๋ exe ํ์ผ ์คํ
1. ์์ฑ๋ ํ์ผ์ ๋๋ธ ํด๋ฆญํ์ฌ ์คํํ๋ค.
2. http://0.0.0.0:8000 ์ผ๋ก ์๋น์ค๊ฐ ์ฌ๋ผ๊ฐ์ ํ์ธํ ์ ์๋ค.
3. ์๋น์ค๊ฐ ์ ์ด์๋จ์ ํ์ธํ ์ ์๋ค.
๐ MacOS์์ ์์ฑํ exe ํ์ผ์ windows์์ ์คํ์ด ๋์ง ์๋๋ค. windows์์ ์คํํ exe ํ์ผ์ windows์์ ์์ฑํด์ผํ๋ค.
๐ windows ์์ pyinstaller ์คํ ์ ์ ์ํ ์ 2๊ฐ์ง
- pyinstaller๋ Python์ Scripts ๋๋ ํ ๋ฆฌ์ ์ค์น๋๋ค. ์ด ๊ฒฝ๋ก๊ฐ Windows ํ๊ฒฝ ๋ณ์ PATH์ ์ถ๊ฐ๋์ง ์์ ๊ฒฝ์ฐ, pyinstaller ๋ช
๋ น์ด๋ฅผ ์ธ์ํ์ง ๋ชปํ ์ ์๋ค.
- Python Scripts ๊ฒฝ๋ก ํ์ธํ๊ณ Windows ํ๊ฒฝ ๋ณ์์ ์ถ๊ฐํ๋ค.
- ์ ์ดํ > ์์คํ > ๊ณ ๊ธ ์์คํ ์ค์ > ํ๊ฒฝ ๋ณ์๋ก ์ด๋ → PATH ๋ณ์๋ฅผ ์ฐพ์ ํธ์งํ ํ, C:\Users\YourUsername\AppData\Local\Programs\Python\Python39\Scripts ๊ฒฝ๋ก๋ฅผ ์ ์ค๋ก ์ถ๊ฐํ๊ณ ์ ์ฅ
- cmd ๋๋ PowerShell ์ ๊ด๋ฆฌ์ ๊ถํ์ผ๋ก ์คํ์์ผ์ pyinstaller๋ฅผ ์คํํ๋ค.
- Windows์์ ๊ด๋ฆฌ์ ๊ถํ์ด ํ์ํ ์ ์๋ค.
'๐ฉ๐ปโ๐ป > python' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[python] socket ๋์ ์ฐ๊ฒฐ (1) | 2024.10.22 |
---|---|
[python] fastapi ์ค์๊ฐ ์คํธ๋ฆฌ๋ฐ ์ด๋ฏธ์ง ์ก์ถ (0) | 2024.10.21 |
[python] TypeError: 'type' object is not subscriptable (1) | 2024.05.02 |
[python] Received response with content-encoding: gzip, but failed to decode it. (0) | 2024.02.19 |
[python] ๋ค์ด๋ฒ ์นดํ ํฌ๋กค๋ง (1) | 2024.01.30 |