ํ๋ผ์ผ๋ง๋ ๋ ์ํผ๋ฅผ ๊ณต์ ํ๊ณ , ๋ ์ํผ์ ๊ฐ๊ฒฉ์ ์ ๊ณตํ๋ ์ปค๋ฎค๋ํฐ์ ๋๋ค.
ํ๋ผ์ผ๋ง๋ฅผ ํตํด ๋ ์ํผ๋ฅผ ๊ณต์ ํ๊ณ , ๊ฐ์ฑ๋น ์๋ ํ๋ผ ์ฑ๊ธฐ์ธ์ ๐ฅ
๋ฐฐ์น์ | ์์ข ํ | ์ด์ง์ค | ์ ์งํ | ๊น์งํ |
---|---|---|---|---|
์ผ๋ณ | ๋ด์ฉ |
---|---|
1~7์ผ์ฐจ (9/1 ~ 9/7) |
- Flask ์คํฐ๋ |
1์ผ์ฐจ (9/1) |
- ์ฃผ์ ์ ์ , ๊ธฐ์ ์คํ ๋ฐ ํ์ ํด ๊ฒฐ์ (Notion, Discord) |
3์ผ์ฐจ (9/3) |
- ์ธ๋ถ ์ปจ์ ๊ธฐํ, ์ธ๋ถ ์๊ตฌ์ฌํญ ์ ์ |
2~5์ผ์ฐจ (9/2/ ~ 9/5) |
- ํ์ ๊ธฐ๋ฅ ๊ฐ๋ฐ |
5์ผ์ฐจ (9/5) |
- ๋ฐฐํฌ - ๊ฐ๋ฐ / ์ด์ ํ๊ฒฝ ๋ถ๋ฆฌ |
5~7์ผ์ฐจ (9/5 ~ 9/7) |
- ๋๋ฒ๊น
- ์ถ๊ฐ ๊ธฐ๋ฅ ๊ตฌํ |
7์ผ์ฐจ (9/7) |
- ๋ณด๊ณ ์ ์์ฑ |
- CHAT CHANNEL
# ๋ผ์ด์ง
# ๊ณต์ง
# ์์ด๋์ด
# ๋ ํผ๋ฐ์ค
# ์ด์
# ์ง๋ฌธ
# ํ์ผ-๋ฐ-์ฝ๋
- VOICE CHANNEL
# ๋ฏธํ
- # ๋ผ์ด์ง: ์์ ๋กญ๊ฒ ํ ๋ก
- # ๊ณต์ง: ๋ฏธํ , ์ผ์ , ํ์๋ก ๊ณต์ง
- # ์์ด๋์ด: ๋ฒ๋ฉ์ด๋ ์์ด๋์ด
- # ๋ ํผ๋ฐ์ค: ์ฐธ๊ณ ํ ๋งํ ์๋น์ค, ์๋ฃ ๋ ํผ๋ฐ์ค
- # ์ด์: ๊ฐ๋ฐ ๋ฐ ํ๋ก์ ํธ ์งํ์ ์์ด ์๊ธฐ๋ ์ด์
- # ์ง๋ฌธ: ์ง์ง ์๋ฌด ์ง๋ฌธ์ด๋
- # ํ์ผ-๋ฐ-์ฝ๋: ํ์ผ ๋ฐ ์ฝ๋ ๊ณต์
- # ๋ฏธํ : ๋ณด์ด์ค & ํ์ด์ค ๋ฏธํ
/ : ๋ฉ์ธ ํ์ด์ง, index.html
โโโโโ /post : ๊ฒ์๊ธ Blueprint
โ โโโโโ /list : ๊ฒ์๊ธ ๋ฆฌ์คํธ
โ โโโโโ /detail/<int:post_id> : ๊ฒ์๊ธ ์์ธ
โ โโโโโ /create : ๊ฒ์๊ธ ์์ฑ (HTTP Method: GET, POST)
โ โโโโโ /modify/<int:post_id> : ๊ฒ์๊ธ ์์ (HTTP Method: GET, POST)
โ โโโโโ /delete/<int:post_id> : ๊ฒ์๊ธ ์ญ์
โ โโโโโ /like/<int:post_id> : ๊ฒ์๊ธ ์ข์์
โโโโโ /comment : ๋๊ธ Blueprint
โ โโโโโ /create/<int:post_id> : ๋๊ธ ์์ฑ (HTTP Method: POST)
โ โโโโโ /modify/<int:comment_id> : ๋๊ธ ์์ (HTTP Method: GET, POST)
โ โโโโโ /delete/<int:comment_id> : ๋๊ธ ์ญ์
โ โโโโโ /like/<int:post_id>/ : ๋๊ธ ์ข์์
โโโโโ /auth : ์ธ์ฆ Blueprint
โโโโโ /signup : ํ์ ๊ฐ์
(HTTP Method: GET, POST)
โโโโโ /login : ๋ก๊ทธ์ธ (HTTP Method: GET, POST)
โโโโโ /logout : ๋ก๊ทธ์์
- ์ฌ์ฉ์๊ฐ ํ์ ์ ๋ณด(์ด๋ฆ, ์ด๋ฉ์ผ, ๋น๋ฐ๋ฒํธ)๋ฅผ ์ ๋ ฅํ์ฌ ์๋ก์ด ๊ณ์ ์ ์์ฑ
- ์ ๋ ฅ๋ ๋น๋ฐ๋ฒํธ๋ ํด์๋ก ์ํธํ๋์ด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ๋จ
- ๋ฑ๋ก๋ ์ฌ์ฉ์๊ฐ ์์ ์ ๊ณ์ ์ผ๋ก ๋ก๊ทธ์ธํ ์ ์์
- ์ธ์ฆ์ ์ํด ์ฌ์ฉ์ ์ด๋ฆ๊ณผ ๋น๋ฐ๋ฒํธ๋ฅผ ํ์ธํ๊ณ ์ธ์ ์ ์ ์ง
- ๋ก๊ทธ์ธํ ์ฌ์ฉ์๊ฐ ๋ ์ํผ๋ฅผ ๊ฒ์๊ธ ํํ๋ก ๊ณต์ ํ ์ ์์
- ์๋ฆฌ ์ด๋ฆ, ๋ ์ํผ ๋ฑ์ ํ ์คํธ ํ๋๋ฅผ ์ ๋ ฅํ๊ณ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ๋จ
- ํ๋จ์ ์ ๋ ํธ ๋ฐ์ค ํ์์ผ๋ก ์ฌ๋ฃ๋ฅผ ์ ํํ ์ ์๊ณ , ์ค๋ ๋๋ ์๋์ ์ ๋ ฅํ๋ฉด ๊ฐ๊ฒฉ์ด ๊ณ์ฐ๋จ
- ๋ง์ฝ ๋ก๊ทธ์ธ ์ํ๊ฐ ์๋๋ผ๋ฉด ๋ก๊ทธ์ธ ์ฐฝ์ผ๋ก ๋ฆฌ๋ค์ด๋ ํธ
- ์์ฑํ ๋ ์ํผ๋ ๋ก๊ทธ์ธํ ์ฌ์ฉ์, ๋ก๊ทธ์ธํ์ง ์์ ์ฌ์ฉ์ ๋ชจ๋๊ฐ ๋ณผ ์ ์์
- ๊ณ์ฐ๋ ๊ฐ๊ฒฉ์ด ๋ ์ํผ ํ๋จ์ ์ถ๋ ฅ๋จ
- ๋ ์ํผ ๊ณต์ ์ ๋๋ ๊ด๋ฆฌ์ ๊ถํ์ ๊ฐ์ง ์ฌ์ฉ์๋ง ํด๋น ๋ ์ํผ ์์ ํ ์ ์์
- ์ ํ๋ ๋ ์ํผ์ ํ๋๋ค(์๋ฆฌ ์ด๋ฆ, ๋ ์ํผ, ์ฌ์ง, ์ํ ์นดํ ๊ณ ๋ฆฌ, ๊ฐ๊ฒฉ)์ ์์ ํ๊ณ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ๋ฐ์ดํธ
- ๋ ์ํผ ๊ณต์ ์ ๋๋ ๊ด๋ฆฌ์ ๊ถํ์ ๊ฐ์ง ์ฌ์ฉ์๋ง ํด๋น ๋ ์ํผ๋ฅผ ์ญ์ ํ ์ ์์
- ์ ํ๋ ๋ ์ํผ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ญ์ ๋จ
- ๋ก๊ทธ์ธํ ์ฌ์ฉ์๋ ๋ ์ํผ์ ์ข์์๋ฅผ ๋๋ฅผ ์ ์์
- ์์ ์ ๋ ์ํผ๋ฅผ ์ข์์ํ ๊ฒฝ์ฐ ๋ถ๊ฐ๋ฅํ๋ค๋ ๋ฉ์ธ์ง๊ฐ ์ถ๋ ฅ๋จ
- ์์ฑ๋ ๋ ์ํผ๊ฐ 10๊ฐ ๋จ์๋ก ํ์ด์ง๋ก ๋๋์ด ๋ ๋๋ง๋จ
- ํ ํ์ด์ง์ ๋ณด์ฌ์ง๋ ๋ ์ํผ ๊ฐ์๋ฅผ ์ค์ ํ ์ ์๊ณ , ์ด์ / ๋ค์ ํ์ด์ง ๋งํฌ ๋๋ ํ์ด์ง ๋ฒํธ๋ฅผ ํตํด ํ์ด์ง๋ฅผ ํ์ํ ์ ์์
- ๊ฒ์ ์ฐฝ์์ ์ ๋ฌ ๋ฐ์ ๋ฐ์ดํฐ๊ฐ ์๋ฆฌ ์ด๋ฆ, ๋ ์ํผ ๋ด์ฉ, ๋ ์ํผ ๊ณต์ ์, ๋๊ธ ๋ด์ฉ, ๋๊ธ ์์ฑ์ ํญ๋ชฉ์์ OR ์กฐ๊ฑด์ผ๋ก ๊ฒ์ํ ์ง๋ฌธ์ ๋ฐํ
- ๋ฐํ๋ ๋ฐ์ดํฐ๋ ๋ฆฌ์คํธ ์กฐํ์ ๊ฐ์ด 10๊ฐ ๋จ์๋ก ๋๋์ด ๋ ๋๋ง๋จ
- ํ ํ์ด์ง์ ๋ณด์ฌ์ง๋ ๋ ์ํผ ๊ฐ์๋ฅผ ์ค์ ํ ์ ์๊ณ , ์ด์ / ๋ค์ ํ์ด์ง ๋งํฌ ๋๋ ํ์ด์ง ๋ฒํธ๋ฅผ ํตํด ํ์ด์ง๋ฅผ ํ์ํ ์ ์์
- ๋ก๊ทธ์ธํ ์ฌ์ฉ์๋ ๋ ์ํผ์ ๋๊ธ์ ๋ฌ ์ ์์
- ๋ก๊ทธ์ธํ์ง ์์๋ค๋ฉด ๋๊ธ ์ฐฝ์ด ๋นํ์ฑํ๋์ด ๋๊ธ์ ๋ฌ ์ ์์
- ๋๊ธ์ ํ ์คํธ ํ๋๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅ๋จ
- ๋๊ธ์ ๋ ์ํผ ํ๋จ์์ ๋ฆฌ์คํธ๋ก ์กฐํํ ์ ์์
- ๋๊ธ ์์ฑ์ ๋๋ ๊ด๋ฆฌ์ ๊ถํ์ ๊ฐ์ง ์ฌ์ฉ์๋ง ํด๋น ๋๊ธ์ ์์ ํ ์ ์์
- ๋๊ธ ์์ฑ์ ๋๋ ๊ด๋ฆฌ์ ๊ถํ์ ๊ฐ์ง ์ฌ์ฉ์๋ง ํด๋น ๋๊ธ์ ์ญ์ ํ ์ ์์
- ๋ก๊ทธ์ธํ ์ฌ์ฉ์๋ ๋๊ธ์ ์ข์์๋ฅผ ๋๋ฅผ ์ ์์
- ์์ ์ ๋๊ธ์ ์ข์์ํ ๊ฒฝ์ฐ ๋ถ๊ฐ๋ฅํ๋ค๋ ๋ฉ์ธ์ง๊ฐ ์ถ๋ ฅ๋จ
๐ recipe-book
โโโโโ ๐ app
โ โโโโโ ๐ api
โ โโโโโ ๐ static
โ โโโโโ ๐ templates
โ โโโโโ ๐ views
โ โโโโโ ๐ __init__.py
โ โโโโโ ๐ forms.py
โ โโโโโ ๐ models.py
โโโโโ ๐ config
โ โโโโโ ๐ __init__.py
โ โโโโโ ๐ default.py
โ โโโโโ ๐ develoment.py
โ โโโโโ ๐ production.py
โโโโโ ๐ logs
โโโโโ ๐ .gitignore
โโโโโ ๐ app.db
โโโโโ ๐ requirements.txt
โโโโโ ๐ README.md
๐ก: Flask ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ด๋ จ๋ ๋๋ ํฐ๋ฆฌ
- ๐ api: ๊ณต๊ณต๋ฐ์ดํฐ API๋ฅผ ํธ์ถํ๋ ํด๋ผ์ด์ธํธ
- ๐ static: ์ด๋ฏธ์ง, css, js ๋ฑ์ ์ ์ ํ์ผ์ ์ ์ฅํ๋ ๋๋ ํฐ๋ฆฌ
- ๐ templates: HTML ํ ํ๋ฆฟ์ ์ ์ฅํ๋ ๋๋ ํฐ๋ฆฌ
- ๐ views: Blueprint ํ์ผ์ ์ ์ฅํ๋ ๋๋ ํฐ๋ฆฌ
- ๐ __init__.py: ์ ํ๋ฆฌ์ผ์ด์ ํฉํ ๋ฆฌ ํจ์๊ฐ ์๋ ํ์ผ
- ๐ forms.py: ๋๋๋ง์ ํ์ํ HTML ์ฝ๋๋ฅผ ์์ฑํ๋ฉฐ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ๋ ฅ ํผ์ ์ ์ํ๊ณ ํ๋๋ฅผ ์ถ๊ฐํ๋ Form ํด๋์ค๊ฐ ์ ์๋์ด ์๋ ํ์ผ
- ๐ models.py: ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ฒด์ ๋ด์ ORM์ ์ด์ฉํ๊ธฐ ์ํ ํด๋์ค๊ฐ ์ ์๋์ด ์๋ ํ์ผ
๐ก: ํ๊ฒฝ ํ์ผ์ ์ ์ฅํ๋ ๋๋ ํฐ๋ฆฌ
- ๐ __init__.py: ํจํค์ง๋ก ์ธ์ํ๊ธฐ ์ํด ์์ฑํ ํ์ผ
- ๐ default.py: ๋ชจ๋ ํ๊ฒฝ์์ ๊ณตํต์ผ๋ก ์ฌ์ฉํ ํ๊ฒฝ ๋ณ์๋ฅผ ์ ์ฅํ๋ ํ์ผ
- ๐ develoment.py: ๊ฐ๋ฐ ํ๊ฒฝ์์ ์ฌ์ฉํ ํ๊ฒฝ ๋ณ์๋ฅผ ์ ์ฅํ๋ ํ์ผ
- ๐ production.py: ์ด์ ํ๊ฒฝ์์ ์ฌ์ฉํ ํ๊ฒฝ ๋ณ์๋ฅผ ์ ์ฅํ๋ ํ์ผ
๐ก: ๋ก๊ทธ ํ์ผ์ ์ ์ฅํ๋ ๋๋ ํฐ๋ฆฌ, ์ด์ ํ๊ฒฝ์์๋ง ์ฌ์ฉ
๐ก: ๊ฐ์ ํ๊ฒฝ์์ ์ค์นํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ํ์ผ๋ก ๋ด๋ณด๋ด ๋ค๋ฅธ ํ๊ฒฝ์์๋ ์ฝ๊ฒ ๊ฐ์ ํ๊ฒฝ์ ๊ตฌ์ถํ ์ ์๊ฒ ํจ
- ๋ณด๋ผ์ ํ ์ด๋ธ์ Many-to-Many ๊ด๊ณ๋ฅผ ์ํด ์์ฑํ ํ ์ด๋ธ
# Factory Function
def create_app():
app = Flask(__name__)
app.config.from_envvar("APP_CONFIG_FILE")
db.init_app(app)
if app.config["SQLALCHEMY_DATABASE_URI"].startswith("sqlite"):
migrate.init_app(app, db, render_as_batch=True)
else:
migrate.init_app(app, db)
- Flask ์ ํ๋ฆฌ์ผ์ด์
์ ์์ฑํ๊ณ ์ด๊ธฐํํ๋ ๋์์ธ ํจํด
- ์ ํ๋ฆฌ์ผ์ด์
๊ฐ์ฒด๋ฅผ Factory Function์ ์ฌ์ฉํด ๋์ ์ผ๋ก ์์ฑํ๊ณ ์ค์
- ์ฌ๋ฌ ๊ฐ์ ์๋ก ๋ค๋ฅธ ์ ํ๋ฆฌ์ผ์ด์ ์ธ์คํด์ค๋ฅผ ๋ง๋ค๊ณ ๋ค๋ฅธ ํ๊ฒฝ(๊ฐ๋ฐ, ํ ์คํธ ํ๋ก๋์ )์์ ์คํํ ์ ์์
- ์ด๋ฅผ ํตํด ๋ณธ ํ๋ก์ ํธ์์ ๊ฐ๋ฐ๊ณผ ์ด์ ํ๊ฒฝ์ ์ค์ ์ ๋ค๋ฅด๊ฒ ํจ
- ์ฅ์
- ํ์ฅ์ฑ ํฅ์
- ์ค์ ๊ณผ ํ๊ฒฝ ๊ด๋ฆฌ์ ์ฉ์ด
- ์ ํ๋ฆฌ์ผ์ด์
๊ฐ์ฒด๋ฅผ Factory Function์ ์ฌ์ฉํด ๋์ ์ผ๋ก ์์ฑํ๊ณ ์ค์
- Flask ๋ธ๋ฃจํ๋ฆฐํธ๋ ์น ์ ํ๋ฆฌ์ผ์ด์
์ ์ฌ๋ฌ ๊ฐ์ ๋
๋ฆฝ์ ์ธ ๋ชจ๋๋ก ๋๋๊ณ , ๊ฐ ๋ชจ๋์ ๊ฐ๋ณ์ ์ผ๋ก ์์
ํ๊ฑฐ๋ ์ฌ์ฌ์ฉํ ์ ์๊ฒ ํด์ค
- ๋ผ์ฐํ ํจ์์ ์งํฉ
- ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ ๋ ์ฝ๋๋ฅผ ๋ ๊ตฌ์กฐํํ๊ณ ๊ด๋ฆฌํ๊ธฐ ์ฝ๊ฒ ๋ง๋ฌ
- ์ฅ์
- ๋ชจ๋ํ
- ๊ฐ ๊ธฐ๋ฅ์ ๋ ๋ฆฝ์ ์ธ ๋ธ๋ฃจํ๋ฆฐํธ๋ก ์ ์ํ์ฌ ๋ชจ๋ํ๋ ์ฝ๋ ์์ฑ์ด ๊ฐ๋ฅํ๋ฏ๋ก ์ ์ง ๋ณด์์ ํ์ฅ์ด ์ฉ์ด
- ๋ผ์ฐํ
๋ถ๋ฆฌ
- URL ๋ผ์ฐํ ์ ๊ฐ ๋ธ๋ฃจํ๋ฆฐํธ ๋ด์์ ๊ด๋ฆฌํ๋ฏ๋ก ์ถฉ๋์ ๋ฐฉ์งํ๊ณ URL ๊ตฌ์กฐ๋ฅผ ๊ตฌ์ฑํ๊ธฐ ์ฌ์
- ์ฝ๋ ์ฌ์ฌ์ฉ
- ๋น์ทํ ๊ธฐ๋ฅ์ ๋ค๋ฅธ ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฌ์ฌ์ฉํ ์ ์์
- ๋ชจ๋ํ
- ๋ณธ ํ๋ก์ ํธ์์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ชฉ์ ์ ๋ฐ๋ผ ๋ชจ๋๋ก ๋ถํ ํด ๋ผ์ฐํ ํจ์๋ฅผ ์์ฑํ์
- ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ธ๋ฃจํ๋ฆฐํธ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํด URL ๊ฒฝ๋ก์ ๋ฐ๋ผ ์ด๋ค ํจ์๊ฐ ์คํ๋ ์ง๋ฅผ ์ ์ํ๋ ๋ฐ ์ฌ์ฉ๋๋ ๋ฐ์ฝ๋ ์ดํฐ
- @app.route ๋ํ URL ๊ฒฝ๋ก์ ํจ์ ๊ฐ์ ๋งคํ์ ์ ์ํ์ง๋ง, @app.route๋ Flask ์ ํ๋ฆฌ์ผ์ด์
๊ฐ์ฒด('app')์ ์ง์ ๋ผ์ฐํ
ํจ์๋ฅผ ์ฐ๊ฒฐํ๋ ๋ฐ ์ฌ์ฉ๋๋ ๋ฐ๋ฉด, @bp.route ๋ฐ์ฝ๋ ์ดํฐ๋ ๋ธ๋ฃจํ๋ฆฐํธ ๊ฐ์ฒด์ ๋ผ์ฐํ
ํจ์๋ฅผ ์ฐ๊ฒฐํ๋ ๋ฐ ์ฌ์ฉ๋จ
- ๋ธ๋ฃจํ๋ฆฐํธ๋ Flask ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ชจ๋ํํ๊ณ ๋ผ์ฐํ ์ ๊ตฌ์กฐํํ๋ ๋ฐ ์ฌ์ฉ๋จ
- ๋ธ๋ฃจํ๋ฆฐํธ๋ฅผ ์ฌ์ฉํ ๋ผ์ฐํ ํจ์๋ค์ ๊ด๋ฆฌํ๋ ๋ฐ ์ ์ฉํ๋ฉฐ ๋ชจ๋ํ, ํ์ฅ ๊ฐ๋ฅ์ ์ฅ์ ์ด ์์
- Front-end ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ์ด ๋ถ๊ฐ๋ฅํด ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ทฐ๋ฅผ ์๋ฒ์์ ์์ฑํด ํด๋ผ์ด์ธํธ์๊ฒ ์ ๋ฌํ๋ ๋ฐฉ์์ธ SSR ์ฌ์ฉ
- ๊ฐ์ฒด์ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ฐ์ ์ํธ ์์ฉ์ ๊ฐ๋จํ๊ฒ ๋ง๋ค์ด์ฃผ๋ ํ๋ก๊ทธ๋๋ฐ ๊ธฐ์ ๋๋ ๋๊ตฌ
- ORM์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ๊ณผ OOP ์ธ์ด ๊ฐ์ ๋ถ์ผ์น๋ฅผ ํด๊ฒฐํ๊ณ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ํ๋ฆฌ์ผ์ด์ ์ฝ๋ ๊ฐ์ ์ํธ ์์ฉ์ ์ถ์ํ
- Python์์๋ SQLAlchemy ์ฌ์ฉ
- ์ฅ์
- ๊ฐ์ฒด์งํฅ ์ฝ๋๋ก ๋น์ฆ๋์ค ๋ก์ง์ ์ง์ค ๊ฐ๋ฅ
- ์ฌ์ฌ์ฉ ๋ฐ ์ ์ง๋ณด์ ํธ๋ฆฌ์ฑ ์ฆ๊ฐ
- DBMS์ ๋ํ ์ข ์์ฑ ์ค์ด๋ฌ
- ๋จ์
- ORM๋ง์ผ๋ก๋ ์๋น์ค ๊ตฌํ์ด ์ด๋ ค์
- ํ๋ก์์ (์์ )๊ฐ ๋ง์ ์์คํ ์์๋ ์ฅ์ ์ ์ทจํ๊ธฐ ์ด๋ ค์
- Workflow
- ๋ฐ์ดํฐ ๋ชจ๋ธ ์ ์: ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ๊ณผ ๋งคํ๋ ๋ฐ์ดํฐ ๋ชจ๋ธ(ํด๋์ค)์ ์ ์
- ORM ์ค์ : ORM์ ์ค์ ํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ ์ ๋ณด์ ๋ฐ์ดํฐ ๋ชจ๋ธ ๊ฐ์ ๋งคํ ๊ท์น์ ์ค์
- CRUD ์์ : ์ ํ๋ฆฌ์ผ์ด์ ์ฝ๋์์ ORM์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ CRUD
- ์ฟผ๋ฆฌ ์คํ: ORM์ ๊ฐ๋ฐ์๊ฐ ์์ฑํ ORM ์ฝ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก SQL ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๊ณ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์กํ์ฌ ์คํ
- ๊ฒฐ๊ณผ ์ฒ๋ฆฌ: ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๋ฐํ๋ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ฒด๋ก ๋ณํํ๊ฑฐ๋ ํ์ํ ์์ ์ ์ํ
- ์์: Python code -> SQL query
# Python code
sub_query = db.session.query(Comment.post_id, Comment.content, User.username)
.join(User, Comment.user_id == User.id).subquery()
-- SQL query
SELECT *
FROM post_list INNER JOIN "User" ON post_list.user_id = "User".id
LEFT OUTER JOIN sub_query ON sub_query.post_id = post_list.id
WHERE post_list.subject ILIKE 'search_value';
- Python์ ์ํ ORM ๋ฐ SQL ํดํท ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- ๋ฐ์ดํฐ๋ฐ์ด์ค์ ์ํธ์์ฉํ๊ธฐ ์ํ ๋๊ตฌ ์ ๊ณต
- ๊ตฌ์ฑ ์์
- Core: ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ํธ์์ฉํ๊ธฐ ์ํ ๊ธฐ๋ณธ ๋๊ตฌ ์ ๊ณต
- ORM: ๋ฐ์ดํฐ๋ฒ ์ด์ค์ Python ํด๋์ค ์ฌ์ด์ ๋งคํ ์ ๊ณต
- SQLAlchemy: ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ์ ํ์ด์ฌ ํด๋์ค๋ก ์ ์
- Session: ์ธ์ ์ ํตํด ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ํ ํธ๋์ญ์ ์ ๊ด๋ฆฌ
- Engine: ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ํ ๋ชจ๋ SQL ์์ ์ด ์ํ๋จ
- Query: SQL ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๊ณ ์คํํ๋ ์ฟผ๋ฆฌ ๋น๋๋ฅผ ์ ๊ณต
- DB ๊ด๋ฆฌ ๋ช
๋ น์ด
- flask db migrate: ๋ชจ๋ธ์ ์์ฑํ๊ฑฐ๋ ๋ณ๊ฒฝํ ๋ ์ฌ์ฉ
- flask db upgrade: ๋ชจ๋ธ์ ๋ณ๊ฒฝ ๋ด์ฉ์ ์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฉํ ๋ ์ฌ์ฉ
- Models.py ํ์ผ์ ORM์ ์ฌ์ฉํด ๋ชจ๋ธ์ ์ ์ํจ
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ๊ณผ ์ํธ์์ฉํ๊ธฐ ์ํ ํด๋์ค๋ฅผ ์ ์
- ํด๋์ค์ ๊ฐ ์์ฑ์ ๋งคํ๋๋ ํ ์ด๋ธ์ Column๊ณผ ์ผ์น
- ๋ฐ์ดํฐ ์ ํ๊ณผ ์ ์ฝ ์กฐ๊ฑด ๋ํ ์ง์
- ์ฌ๋ฃ ํ ์ด๋ธ์ ๋ฐ์ดํฐ ์์ค๋ฅผ Open API๋ฅผ ๋ฐ์์ ๊ตฌ์ฑ
- ๊ธฐํ ์ ๋ฆฌํ ์ผ ์๋น์ค (e.g., ์ ์ธ๊ณ ์ด๋งํธ๋ชฐ) ๋ด ๋ธ๋๋ ์ปค๋ฎค๋ํฐ๋ฅผ ๊ตฌํํ๊ณ ์ ํ๊ธฐ ๋๋ฌธ์ ์ง์ ๊ฐ๊ฒฉ ์ ๋ณด๋ฅผ ๋ฐ์์ค๋ ๊ฒ์ด ๋ฐ์ดํฐ์ ์ ๋ขฐ์ฑ๊ณผ ์ค์๊ฐ์ฑ์ ๋ณด์ฅํ ์ ์๋ ๋ฐฉ๋ฒ์ด๋ผ๊ณ ์๊ฐ
- ๊ทธ๋ฌ๋ SSG, Coupang, Lotte Mall ๋ฑ์ Open API๋ ์ ์ ํด์๋ ํ๋งค์์๊ฒ๋ง ์ด๋ ค์์ด ๊ณต๊ณต๋ฐ์ดํฐ API๋ฅผ ๋ฐ์์ค๊ธฐ๋ก ํจ
- ํ๊ตญ๋์์ฐ์ํ๊ณต์ฌ์ KAMIS์์ ์ ๊ณตํ๋ Open API์์ ์ผ๋ณ ํ๋ชฉ๋ณ ์๋งค ๊ฐ๊ฒฉ ์ ๋ณด๋ฅผ ํธ์ถ
- ๊ณต์์ผ๋ก ์ง์ํ๋ Client๋ ์์๊ฐ ์์ด์ ์น์ ์๋ API ํธ์ถ ํด๋ผ์ด์ธํธ๋ฅผ ์ฐธ๊ณ ํด ์ปค์คํ
- Example Code
client = KamisOpenApi(
CertificationPair(
cert_key="๏ฟฝcert_key", cert_id="cert_id"
)
)
# API ํธ์ถ
daily_sales_list = client.daily_sales_list()
info = dict()
food = Food()
# ๋ฐ์ดํฐ Parsing
def get_food():
# 215๊ฐ ํ์
for i in range(215):
"""sumary_line
์นดํ
๊ณ ๋ฆฌ๊ฐ ์ถ์ฐ๋ฌผ, ์์ฐ๋ฌผ์ผ ๊ฒฝ์ฐ์ ๋๋จธ์ง์ผ ๊ฒฝ์ฐ๋ฅผ ๋๋ ํ์ฑ
"""
if daily_sales_list.prices[i].category_code in ("500", "600"):
name = daily_sales_list.prices[i].product_name
info[name] = [
daily_sales_list.prices[i].date_price_dict["๋น์ผ"],
daily_sales_list.prices[i].unit,
daily_sales_list.prices[i].category_name,
daily_sales_list.prices[i].category_code,
]
else:
name = daily_sales_list.prices[i].product_name.split("/")
info[name[0]] = [
daily_sales_list.prices[i].date_price_dict["๋น์ผ"],
daily_sales_list.prices[i].unit,
daily_sales_list.prices[i].category_name,
daily_sales_list.prices[i].category_code,
]
return info
parsed_data = get_food()
- Open API์์ ๋ฐ์์จ ๋ฐ์ดํฐ์ ์ด๋งํธ๋ชฐ์์ ์กฐ์ฌํ ๋ฐ์ดํฐ๋ฅผ ํผํฉํด DB์ ์ฝ์ ํ๊ณ , ๋ ์ํผ ์์ฑ ์์ฒญ์ด ๋ค์ด์ค๋ฉด ์ฌ๋ฃ ํ ์ด๋ธ์ ์ ๋ณด๋ฅผ ํ ํ๋ฆฟ ์์ง์ผ๋ก ๋๊น
- ์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ์ฌ๋ฃ์ ์๋ ํน์ ์ค๋์ด Flask๋ก ๋์ด์ค๋ฉด ์ ํจ์ฑ ๊ฒ์ฌ ํ ๊ฐ๊ฒฉ์ ๊ณ์ฐํ๋ ํจ์์์ ์ฒ๋ฆฌ๋จ
๋ ์ํผ ๊ฐ๊ฒฉ = (์ฌ๋ฃ ๊ฐ๊ฒฉ * ์๋ ํน์ ์ค๋ / ๋จ์ ) + (์ฌ๋ฃ ๊ฐ๊ฒฉ * ์๋ ํน์ ์ค๋ / ๋จ์ ) + ...
- Flask ์ ํ๋ฆฌ์ผ์ด์ ์์ HTTP ์์ฒญ๊ณผ ๊ด๋ จ๋ ์ ๋ณด๋ฅผ ์ฒ๋ฆฌํ๋ ค๋ฉด 'request' ๊ฐ์ฒด๋ฅผ ์ฌ์ฉ
- ์ด ๊ฐ์ฒด๋ ํ์ฌ ์์ฒญ๊ณผ ๊ด๋ จ๋ ๋ค์ํ ์ ๋ณด๋ฅผ ์ ๊ณตํ๋ฉฐ HTTP ๋ฉ์๋, URL, ํค๋, ์ฟผ๋ฆฌ ๋ฌธ์์ด, ํผ ๋ฐ์ดํฐ, JSON ๋ฐ์ดํฐ ๋ฐ ๊ธฐํ ์์ฒญ ๋ฐ์ดํฐ์ ์ ๊ทผํ ์ ์๋๋ก ๋์์ค
- ๋ณธ ํ๋ก์ ํธ์์๋ Blueprint, API ํธ์ถ ๋ชจ๋์์ ์ฃผ๋ก ์ฌ์ฉ
- 'request' ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํด ์ป์ ์ ์๋ ์ฃผ์ ์ ๋ณด
- HTTP ๋ฉ์๋:
request.method
๋ฅผ ํตํด ํ์ฌ ์์ฒญ์ HTTP ๋ฉ์๋๋ฅผ ์ป์ ์ ์์- ์๋ฅผ ๋ค์ด, GET, POST, PUT, DELETE ๋ฑ์ ๋ฉ์๋๊ฐ ๋ฐํ๋จ
- URL ์ ๋ณด:
request.url
์ ํ์ฌ ์์ฒญ์ ์ ์ฒด URL์ ๋ํ๋ - ํค๋ ์ ๋ณด:
request.headers
๋ HTTP ์์ฒญ ํค๋์ ๋์ ๋๋ฆฌ๋ฅผ ๋ฐํ- ์๋ฅผ ๋ค์ด,
request.headers['User-Agent']
๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์ ์์ด์ ํธ(User-Agent) ํค๋๋ฅผ ์ป์ ์ ์์
- ์๋ฅผ ๋ค์ด,
- ์ฟผ๋ฆฌ ๋ฌธ์์ด:
request.args
๋ URL์ ์ฟผ๋ฆฌ ๋ฌธ์์ด ํ๋ผ๋ฏธํฐ๋ฅผ ๋์ ๋๋ฆฌ๋ก ๋ฐํ- ์๋ฅผ ๋ค์ด,
/search?query=example
URL์์request.args['query']
๋ "example" ๊ฐ์ ๋ฐํ
- ์๋ฅผ ๋ค์ด,
- ํผ ๋ฐ์ดํฐ:
request.form
์ POST ์์ฒญ์ ํผ ๋ฐ์ดํฐ๋ฅผ ๋์ ๋๋ฆฌ๋ก ๋ฐํ, HTML ํผ์์ ์ ์ถ๋ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ๋ ์ ์ฉ - JSON ๋ฐ์ดํฐ:
request.get_json()
์ ์ฌ์ฉํ์ฌ JSON ์์ฒญ ๋ณธ๋ฌธ์ ํ์ฑํ๊ณ JSON ๋ฐ์ดํฐ๋ฅผ ์ป์ ์ ์์ - ํ์ผ ์
๋ก๋: ํ์ผ ์
๋ก๋๊ฐ ์๋ POST ์์ฒญ์ ๊ฒฝ์ฐ,
request.files
๋ฅผ ์ฌ์ฉํ์ฌ ์ ๋ก๋๋ ํ์ผ์ ์ ๊ทผํ ์ ์์ - ์ธ์
์ ๋ณด:
request.session
์ ์ฌ์ฉํ์ฌ ํ์ฌ ์์ฒญ๊ณผ ๊ด๋ จ๋ ์ธ์ ๋ฐ์ดํฐ์ ์ ๊ทผํ ์ ์์
- HTTP ๋ฉ์๋:
- Flask ์ ํ๋ฆฌ์ผ์ด์ ์์ ์น ํผ์ ์ฝ๊ฒ ์ฒ๋ฆฌํ๊ธฐ ์ํ ํ์ฅ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- ์น ํผ์ ์ฝ๊ฒ ์์ฑํ๊ณ , Form validation, ๋ฐ์ดํฐ ์์ง, ์๋ฌ ์ฒ๋ฆฌ ๋ฑ์ ํ ์ ์์
- CSRF(Cross-Site Request Forgery) ๋ฐฉ์ง ๊ฐ์ ๋ณด์ ๊ธฐ๋ฅ๋ ๋ด์ฅ๋์ด ์์
- CSRF ํ ํฐ ์ฝ๋๋ฅผ ์ฝ์ ํด์ผ ์ ์์ ์ผ๋ก ๋ฐ์ดํฐ ์ ์ก ๊ฐ๋ฅ
- ์ฌ์ฉ ๋ฐฉ๋ฒ
- FlaskForm์ ์์ ๋ฐ๋ Form ํด๋์ค๋ฅผ ์ ์
- View์ ๋ผ์ฐํ
ํจ์์์ Form ์ธ์คํด์ค๋ฅผ ์์ฑํ๊ณ ์ฌ์ฉ
- POST ์์ฒญ์ผ ๋ ๊ฒ์ฆ ์์ ๋ฑ
- HTML ํ ํ๋ฆฟ ํ์ผ์์ ์น ํผ์ ๋ ๋๋งํ๊ณ ์ฌ์ฉ์์๊ฒ ์ ๋ ฅ์ ๋ฐ์
- SQLAlchemy ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ Pagination ๊ฐ์ฒด ์ฌ์ฉ
- ๋ ์ํผ ๋ฆฌ์คํธ๊ฐ ๋ด๊ธด ๊ฐ์ฒด์ pagenate ํจ์ ์ ์ฉ -> Pagination ๊ฐ์ฒด ๋ฐํ๋จ
- Pagenation ๊ฐ์ฒด ์์ฑ
ํญ๋ชฉ | ์ค๋ช | ์์ ๊ฐ |
---|---|---|
items | ํ์ฌ ํ์ด์ง์ ํด๋นํ๋ ๊ฒ์๋ฌผ ๋ฆฌ์คํธ | [<Post 1>, <Post 2>, โฆ] |
total | ๊ฒ์๋ฌผ ์ ์ฒด ๊ฐ์ | 124 |
per_page | ํ์ด์ง ๋น ๋ณด์ฌ์ค ๊ฒ์๋ฌผ ๊ฐ์ | 10 |
page | ํ์ฌ ํ์ด์ง ๋ฒํธ | 7 |
iter_pages | ํ์ด์ง ๋ฒ์ | [1, 2, 3, โฆ, 25] |
prev_num / next num | ์ด์ / ๋ค์ ํ์ด์ง ๋ฒํธ | ํ์ฌ ํ์ด์ง๊ฐ 5์ธ ๊ฒฝ์ฐ 4 / 6 |
has_prev / has_next | ์ด์ / ๋ค์ ํ์ด์ง ์กด์ฌ ์ฌ๋ถ | True / False |
- ์น์์ ํผ์ ์ ์ํ๊ณ ๊ฒ์ฆํ๊ธฐ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- ์น ํผ์ ์ฝ๊ฒ ์์ฑํ๊ณ , ์ฌ์ฉ์๋ก๋ถํฐ ๋ฐ์ดํฐ๋ฅผ ์์งํ๊ณ ๊ฒ์ฆํ ์ ์์
- ํ์๊ฐ์ ์ ์ํด FlaskForm์ ์์ ๋ฐ์ UserCreateForm ์์ฑ
- PasswordField, EmailField์ import
- PasswordField: ์ฌ์ฉ์์ ๋น๋ฐ๋ฒํธ๋ฅผ ์
๋ ฅ๋ฐ์ ๋ ์ด ํ๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋น๋ฐ๋ฒํธ ์
๋ ฅ์ ์์ ํ๊ฒ ์ฒ๋ฆฌํ๊ณ Validation๋ ํ ์ ์์
- ๋ผ์ฐํ ํจ์์์ generate_password_hash ํจ์๋ก ์ํธํ
- EmailField: StringField์ ๋์ผํ์ง๋ง ํ ํ๋ฆฟ ์๋ ๋ณํ์ผ๋ก ์ฌ์ฉ์ <input type="email">๋ก ๋ณํ๋จ
- PasswordField: ์ฌ์ฉ์์ ๋น๋ฐ๋ฒํธ๋ฅผ ์
๋ ฅ๋ฐ์ ๋ ์ด ํ๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋น๋ฐ๋ฒํธ ์
๋ ฅ์ ์์ ํ๊ฒ ์ฒ๋ฆฌํ๊ณ Validation๋ ํ ์ ์์
- FlaskForm์ ์์ ๋ฐ์ UserLoginForm ์์ฑ
- ๋ก๊ทธ์ธ ์ฌ๋ถ๋ g ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํด ๋ผ์ฐํ ํจ์์์ ๊ฒ์ฆ
- ๋ก๊ทธ์ธ ๋ผ์ฐํ
ํจ์์์ session, g๋ฅผ import
- session
- Flask ์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ ๊ด๋ฆฌ, ์ฌ์ฉ์๋ณ๋ก ๊ณ ์ ํ ๋ฐ์ดํฐ ์ ์ฅํ๋ ๊ฐ์ฒด
- Flask๊ฐ ์ธ์ ๋ฐ์ดํฐ๋ฅผ ์ํธํํด ๋ณด์ ์ ์ง
- ์ธ์ ์ ์ฌ์ฉํ๋ฉด ์ฌ์ฉ์์ ๋ก๊ทธ์ธ ์ํ, ์ฅ๋ฐ๊ตฌ๋ ๋ด์ญ ๋ฑ์ ์ ๋ณด๋ฅผ ์ ์งํ๊ณ ์ํ๋ฅผ ์ถ์ ๊ฐ๋ฅ
- g
- Flask ์ ํ๋ฆฌ์ผ์ด์ ๋ด์์ ๊ธ๋ก๋ฒํ๊ฒ ์ฌ์ฉ๋๋ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ธฐ ์ํ ๊ฐ์ฒด
- ๋ชจ๋ ์์ฒญ์์ ๊ณต์ ๋๋ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ๊ณ , ๋ค๋ฅธ ํจ์๋ ๋ทฐ์์ ์ด ๋ฐ์ดํฐ์ ์ ๊ทผ ๊ฐ๋ฅ
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ, ํ์ฌ ์ฌ์ฉ์ ์ ๋ณด, ๋ก๊ทธ ๊ธฐ๋ก ๋ฑ๊ณผ ๊ฐ์ด ์ฌ๋ฌ ๊ณณ์์ ์ฌ์ฉ๋๋ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ ๋ ์ ์ฉ
- session
- AWS Lightsail ์๋น์ค๋ฅผ ์ฌ์ฉํด ๋ฐฐํฌ
- ์ ๋ ดํ๊ณ , ์์ฃผ ๊ฐ๋จํ๊ฒ ๋ฐฐํฌ๊ฐ ๊ฐ๋ฅํด ์ ํํ์์
- RAM: 1GB
- vCPU: 2๊ฐ
- SSD: 40GB
- OS: Ubuntu 20.04
- Private key ๋ฐ๊ธ ํ ๋ค์๊ณผ ๊ฐ์ ๋ช ๋ น์ด๋ก ์๋ฒ์ ์ ์
ssh -i ~/PrivateKeys/ssgrecipe.pem ubuntu@static_ip
- git ๋ช ๋ น์ด๋ก clone ํด์จ ๋ค์ requirements.txt๋ฅผ ํตํด ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ค์น -> ๊ฐ๋ฐ ํ๊ฒฝ๊ณผ ๊ฐ์ ํ๊ฒฝ์ผ๋ก ์๋ฒ ๊ตฌ์ฑ
git clone https://github.com/seungwonbased/ssg-recipe-project.git
- ์๋ฒ ํ๊ฒฝ ๋ณ์ ์ค์
- ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ด๊ธฐํ ๋ฐ migrate, upgrade
- ๊ฐ๋ฐ / ์ด์ ํ๊ฒฝ์ ๋๋๊ธฐ ์ํด config ํ์ผ ๋ํ ๋ค์๊ณผ ๊ฐ์ด ๋ถ๋ฆฌ
๐ config
โโโโโ ๐ __init__.py
โโโโโ ๐ default.py
โโโโโ ๐ develoment.py
โโโโโ ๐ production.py
- ๊ฐ๋ฐ ํ๊ฒฝ์์๋ ๊ทธ๋ฅ 'flask run' ๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํด ์ ํ๋ฆฌ์ผ์ด์
์ ๊ตฌ๋ํจ
- ์ด๋ ์ฃผ๋ก ๊ฐ๋ฐ์ด๋ ๋๋ฒ๊น ๋ชฉ์ ์ผ๋ก ์ฌ์ฉ๋จ
- ๋จ์ผ ์ค๋ ๋๋ก ๋์ํ๋ฉฐ ๋ณ๋ ฌ ์ฒ๋ฆฌ์ ๋์ ํธ๋ํฝ์ ์ฒ๋ฆฌํ๊ธฐ์๋ ์ ํฉํ์ง ์์
- ๋ณด์๊ณผ ์ฑ๋ฅ ์ธก๋ฉด์์๋ ์ด ๋ฐฉ์์ผ๋ก ์ง์ ์ธํฐ๋ท์ผ๋ก ๋ ธ์ถํ๋ ๊ฒ์ ๊ถ์ฅ๋์ง ์์
- ์คํ๊ณผ ์ค์ ์ ๋งค์ฐ ํธ๋ฆฌํจ
- ์ด์ ์๋ฒ์์๋ WSGI์ Web Server๋ฅผ ์ฌ์ฉ
- ์น ์๋ฒ์ ์ ํ๋ฆฌ์ผ์ด์ ์๋ฒ(WSGI)๋ฅผ ๋ถ๋ฆฌํ์ฌ ๋์ ์ฑ๋ฅ, ํ์ฅ์ฑ, ๋ณด์ ์ ๊ณต
- WSGI๋ ๋์ ์ปจํ ์ธ ์์ฑ ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ๋ก์ง ์คํ ๋ด๋น
- Web Server๋ ์ ์ ํ์ผ ์๋น, ๋ก๋ ๋ฐธ๋ฐ์ฑ, ๋ณด์ ์ค์ ๋ฑ์ ์ฒ๋ฆฌ
- Python ์น ์ ํ๋ฆฌ์ผ์ด์ ๊ณผ ์น ์๋ฒ ๊ฐ์ ํ์คํ๋ ์ธํฐํ์ด์ค
- WSGI๋ ์น ์๋ฒ์ ์น ์ ํ๋ฆฌ์ผ์ด์ ํ๋ ์์ํฌ ๋๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ถ๋ฆฌํด ๊ฐ๋ฐ์๊ฐ ์๋ก ๋ค๋ฅธ ์๋ฒ ๋ฐ ์ ํ๋ฆฌ์ผ์ด์ ์ ์กฐํฉํ์ฌ ์ฌ์ฉํ ์ ์๋๋ก ์ค๊ฐ ๊ณ์ธต ์ญํ ์ ํจ
- ์ฆ, Python์ผ๋ก ์์ฑ๋ ์ฌ๋ฌ ์น ์ ํ๋ฆฌ์ผ์ด์ ํ๋ ์์ํฌ(e.g., Flask, Django ๋ฑ)์ ์น ์๋ฒ๋ฅผ ํตํฉํ๋ ๋ฐ ์ฌ์ฉ๋จ
- ๊ตฌํ์ฒด๋ก๋ uwsgi, Gunigcorn ๋ฑ์ด ๋ฑ์ฅํ๋ฉด์ ํจ์ฌ ํจ์จ์ ์ธ ๋์ ๋ฆฌ์์ค ์๋น์ด ๊ฐ๋ฅํด์ง
- Flask์ WSGI๋ฅผ ๋ถ์ฌ WAS๋ฅผ ์ด์ฉํ๊ฒ ๋จ
- Flask๋ WSGI ์ ํ๋ฆฌ์ผ์ด์ ์ด๊ณ , WSGI ์๋ฒ์์ ์์ฒญ์ ๋ฐ์ ๋์
- WSGI์ ๊ตฌํ์ฒด
- ์ด์์ ์ํด ์ด์ ์๋ฒ์ WSGI์ธ Gunicorn ์ค์น
- Gunicorn์ ๋ฆฌ๋ ์ค์์ ์๋น์ค๋ก ๋ฑ๋กํ๊ธฐ ์ํด ํ๊ฒฝ ๋ณ์ ํ์ผ ์์ฑ ๋ฐ ์๋น์ค ํ์ผ ์์ฑ
- ์๋น์ค ์๋ ์คํ ์ค์
sudo systemctl enable ssgrecipe.service
- ์น ์๋ฒ๋ ํด๋ผ์ด์ธํธ๋ก๋ถํฐ HTTP ์์ฒญ์ ์์ ํ๊ณ , ์ ์ ํ์ผ(HTML, CSS, ์ด๋ฏธ์ง ๋ฑ)์ ์๋น์คํ๋ฉฐ, ๋์ ์ปจํ ์ธ ๋ฅผ ์์ฑํ๋ ์น ์ ํ๋ฆฌ์ผ์ด์ ์๋ฒ๋ ์ ํ๋ฆฌ์ผ์ด์ ์๋ฒ์ ์์ฒญ์ ์ ๋ฌํ๋ ์ญํ
- ์ฃผ๋ก ์ ์ ์ปจํ ์ธ ๋ฅผ ์๋น์คํ๋ ์ญํ ์ ํ๋ฉฐ, ์น ์๋ฒ๋ก๋ Apache, Nginx, IIS ๋ฑ์ด ์์
- WSGI ์๋ฒ๋ง ๊ตฌ๋ํด๋ ์ ํ๋ฆฌ์ผ์ด์
์ด ์๋ํ๋ ๋ฐ์๋ ๋ฌธ์ ๊ฐ ์์, ๊ทผ๋ฐ ์ WAS ์์ ์น ์๋ฒ๋ฅผ ๋๋์ง?
- ๋ก๋ ๋ฐธ๋ฐ์ฑ: ์ฌ๋ฌ ์น ์๋ฒ ์ธ์คํด์ค ๊ฐ์ ํธ๋ํฝ์ ๋ถ์ฐ
- ๋ณด์ ๋ฐ ์ธ์ฆ: SSL/TLS ์ธ์ฆ์ ๊ด๋ฆฌ ๋ฐ ๋ณด์ ์ค์ ์ ์ฒ๋ฆฌ
- ๋ฆฌ๋ฒ์ค ํ๋ก์: ๋์ ์ปจํ ์ธ ๋ฅผ ์์ฑํ๋ ์ ํ๋ฆฌ์ผ์ด์ ์๋ฒ๋ก ์์ฒญ์ ์ ๋ฌ
- ์ฌ์ค ์์ ๊ฐ์ ๊ธฐ๋ฅ๋ ์ด์ WSGI, WAS์์ ์ง์ํ์ง๋ง, ๊ฒฐ์ ์ ์ผ๋ก WAS๋ ์ ์ฃฝ์
- ์ ํ๋ฆฌ์ผ์ด์ ๋ก์ง์ด ๋์ํ๊ธฐ ๋๋ฌธ
- ๋ฐ๋ผ์ ์น ์๋ฒ, WAS๋ฅผ ๋ ๋ค ๋๊ณ ์ ์ ๋ฆฌ์์ค๊ฐ ๋ง์ด ์ฌ์ฉ๋๋ฉด ์น ์๋ฒ๋ฅผ ์ฆ์คํ๊ณ , ์ ํ๋ฆฌ์ผ์ด์ ๋ฆฌ์์ค๊ฐ ๋ง์ด ์ฌ์ฉ๋๋ฉด WAS ์ฆ์ค
- ์ด์ ์๋ฒ์ ์น ์๋ฒ๋ก Nginx๋ฅผ ์ค์น
- ๋น๋๊ธฐ ๊ธฐ๋ฐ ๊ตฌ์กฐ๋ผ ๋ ์ ์ ๋ฆฌ์์ค๋ฅผ ์ฌ์ฉํด์ ์์ฒญ์ ์ฒ๋ฆฌํ ์ ์์
- Nginx์ ์ค์ ์ ๋ณ๊ฒฝ
- 80 ํฌํธ๋ก ์๋น์คํ๋๋ก ํจ
- static ๊ฒฝ๋ก๋ฅผ ์ง์ ํด ์ ์ ๋ฆฌ์์ค๋ฅผ ์๋นํ ์ ์๋๋ก ํจ
- ์ด์ ํ๊ฒฝ์์๋ ๋ณด์ ์ด์๋ก FLASK_DEBUG๊ฐ False๋ก ๋์ด์์ผ๋ฏ๋ก ๋ก๊ทธ๋ฅผ ํ์ผ๋ก ์ถ๋ ฅํ๋๋ก ํจ
- ์ถ๋ ฅ๋๋ ๋ก๊ทธ ๋ ๋ฒจ์ 2๋จ๊ณ INFO, ์ผ๋ฐ์ ์ธ ์ ๋ณด๋ฅผ ์ถ๋ ฅ
- ์ด์ ์๋ฒ์์๋ ์ฃผ๋ก INFO ๋ ๋ฒจ, ๊ฐ๋ฐ ์๋ฒ์์๋ DEBUG ๋ ๋ฒจ๋ก ์ค์ ํจ
- ์ฑ๋ฅ์ ์ธ ๋ถ๋ถ์ ์ํฅ์ ๋ผ์น๊ธฐ ๋๋ฌธ
- ์ด์ ์๋ฒ์์๋ ์ฃผ๋ก INFO ๋ ๋ฒจ, ๊ฐ๋ฐ ์๋ฒ์์๋ DEBUG ๋ ๋ฒจ๋ก ์ค์ ํจ
- ์ด์ ์๋ฒ์์๋ SQLite๊ฐ ์๋ ์คํ์์ค RDBMS์ธ PostgreSQL๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ๊ฒฐ์
- ์ฌ๋ฌ ๊ฐ์ง ์ด์ ๊ฐ ์๋๋ฐ, ๋ํ์ ์ธ ์ด์ ๋
- SQLite๋ ์๋ฒ๋ฅผ ๋ฐ๋ก ๋๊ณ ํต์ ํ ์ ์๊ธฐ ๋๋ฌธ์ ์ธ์คํด์ค๊ฐ ์๋ ๋ก์ปฌ์์ DB๋ฅผ ์ด์ฉํด์ผ ํจ
- ์์ง์ ๋ฐ์ดํฐ๋ฅผ ๋๊ท๋ชจ๋ก ์ฒ๋ฆฌํ ์ผ์ ์์ง๋ง ์ด์ ์๋ฒ์ ๋ฐฐํฌํ ๋งํผ ๊ทธ ๋ถ๋ถ์ ๊ณ ๋ คํ๊ณ ์ถ์์, SQLite๋ ๋์์ฑ์ ์ ํ์ด ์์ด ์ด์ ์๋ฒ์๋ ๋ถ์ ํฉ
- ์ฌ๋ฌ ๊ฐ์ง ์ด์ ๊ฐ ์๋๋ฐ, ๋ํ์ ์ธ ์ด์ ๋
- AWS์์ PostgreSQL RDS ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์์ฑ
- ORM์ ๋ง๋ฒ ์ผ๋ก config ํ์ผ์์ DB ์๋ํฌ์ธํธ, user, pw, url ์์ ๋ง์ผ๋ก ์ค์ ์๋ฃ
- ์คํค๋ง๋ ์ฟผ๋ฆฌ, ์ ํ๋ฆฌ์ผ์ด์ ์ฝ๋๋ฅผ ๋จ ํ ๊ฐ๋ ๋ณ๊ฒฝํ์ง ์์
- ๊ฐ๋์ ๋๋ฌผ์ ํ๋ฆผ
- ์๋ฒ์์ ๋ค์ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํ๋ฉด ๋ง์ด๊ทธ๋ ์ด์ ์๋ฃ!
flask db init
flask db migrate
flask db upgrade
sudo systemctl restart ssgrecipe.service
โ : ํด๊ฒฐ ์ด์ โ: ๋ฏธํด๊ฒฐ ์ด์
- ์ํ ๊ฐ๊ฒฉ ์ ๋ณด๋ฅผ ๋ฐ์์ค๋ Open API์์ ์๋๋ ์ ์์ ์ผ๋ก ๋ฐ์ดํฐ ํธ์ถ์ด ๋์์
- ์ด๋ ์๊ฐ๋ถํฐ ๋ฐ์ดํฐ ํธ์ถ์ด ์คํจํ๋๋, ์์ ๋์ํ์ง ์๊ธฐ ์์
- ์์ธ์ Flask ์ ํ๋ฆฌ์ผ์ด์
์คํ์ ํธ์ถ๋๋ API ํธ์ถ ํด๋ผ์ด์ธํธ์ ์ด์ํ ๋ฃจํ๋ฅผ ์ง๋์ ์ผ์ผ ํธ์ถ ํ๋ ์ด๊ณผ๊ฐ ๋ ๊ฒ
โ ํด๊ฒฐ: API ํธ์ถ ํด๋ผ์ด์ธํธ๋ฅผ ์์ ํ๊ณ , ์ฌ์ค์ API ํธ์ถ์ ํ ๋ฒ๋ง ํด๋ ๋๊ธฐ ๋๋ฌธ์ ๋ณ๋์ ๋ชจ๋๋ก ์์ฑํ๋ ๋ฐฉํฅ์ผ๋ก ๋ณ๊ฒฝ
- ์ฌ์ฉ์์๊ฒ ๋ ์ํผ๋ฅผ ์ ๋ ฅ๋ฐ์ ๋, ํ๋จ์์ Select box๋ก ์ฌ๋ฃ๋ฅผ ์ ๋ ฅ ๋ฐ์
- ํ์ง๋ง ์ ํ๋ ๊ฐ์ด ๊ณ์ None์ผ๋ก ๋์ด์ด, ๋ฐ๋ฉด ์๋์ ์
๋ ฅํ๋ Text input์ ์ ์์ ์ผ๋ก ๋์ด์ด
โ ํด๊ฒฐ: Form์ ์ ์ํ ๋ ์๋์ ํด๋นํ๋ ํ๋๋ Text input์ด๊ธฐ ๋๋ฌธ์ StringField ๊ฐ์ฒด๊ฐ ๋์ด์ผ ๋๋ ๊ฒ์ ๋ง์์ง๋ง, ์ฌ๋ฃ๋ Select box์ด๊ธฐ ๋๋ฌธ์ Form์์ ํ๋๊ฐ SelectField์ฌ์ผ ๋์
- ๋ ์ํผ๋ง๋ค ๋ค์ด๊ฐ๋ ์ฌ๋ฃ์ ์๊ฐ ๋ค์ํ๊ธฐ ๋๋ฌธ์ Select box์ ์๊ฐ ๋์ ์ผ๋ก ์ถ๊ฐ๋์ผ๋ฉด ํจ
โ ํด๊ฒฐ: ํ ํ๋ฆฟ ์์ง ํ๋จ์ JS ์ฝ๋๋ฅผ ์์ฑํจ, ์ถ๊ฐ ๋ฒํผ ํด๋ฆญ ์ createElement ํจ์๋ฅผ ํธ์ถํด <div> ํ๊ทธ ์์ฑ
- 8.2.์์ ํด๊ฒฐํ ๋์ ์์ฑํ Select Box์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๋๋ฐ ์คํจ
- ๋ญ๊ฐ ๋์ ์ผ๋ก ๋ฐฐ์ด๋ก ๋ฐ๊ณ ์ถ์๋ฐ, ๋๋ฌด์ง ๋ฐฉ๋ฒ์ ๋ชจ๋ฅด๊ฒ ์
โ ๋ฏธํด๊ฒฐ: ๊ฒฐ๊ตญ ํ์ฌ๊น์ง Select box ๊ฐ์๋ฅผ ์ ์ ์ผ๋ก ์ค์ ํ๊ณ ํ์ ๋ ์์ ์ฌ๋ฃ๋ง ์ ๋ ฅ๋ฐ์ ์ ์์, ํ ํ๋ฆฟ ์์ง ๋ฌธ๋ฒ์ด๋ , HTML ์ฝ๋๋ ๋ ๋ฅ๋ค์ด๋ธ ํด๋ณผ ๊ฒ
- ๋ ์ํผ์ ์ด๋ฏธ์ง๋ฅผ ์ ๋ก๋ํ๋ฉด ๊ตฌ์ฑํด๋ ๋๋ ํฐ๋ฆฌ์ ์ ์์ ์ผ๋ก ์ฝ์ ์ ๋์ง๋ง ๋ ์ํผ๋ฅผ ์กฐํํ ๋๋ HTML์์ ํด๋น ๊ฒฝ๋ก์ ์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ฌ ์ ์์, ํ์ด์ง ์์ค๋ฅผ ๋ณด๋ฉด ๊ฒฝ๋ก๋ ๋ถ๋ช ๋ง๋๋ฐ ์์ค๋ฐ์ค๊ฐ ๋ธ
- DB์ ์ ์์ ์ผ๋ก ์ฝ์
์ ๋์ง๋ง ๋ถ๋ฌ์ฌ ์ ์์
โ ํด๊ฒฐ: ์ด๋ฏธ์ง ์ ์ฅ ๊ฒฝ๋ก๋ฅผ ์์๋ก ์ฃผ๋ฉด ์ ๋๊ณ , static ๋๋ ํฐ๋ฆฌ ์์ ์์ฑํด์ผ ์ด๋ฏธ์ง๋ฅผ ์ ์์ ์ผ๋ก ์๋นํ ์ ์์
- ์ฒ์ ๊ฐ๋ฐ์ ์์ํ ๋๋ ์ด์ ์๋ฒ๋ก ๋ฐฐํฌ๊น์ง๋ ํ ์๊ฐ์ด ์์ด์ ๋ก์ปฌ์์ ๊ฐ๋ณ๊ฒ ์ธ ์ ์๋ ํด๋ผ์ด์ธํธ ์ ์ฉ DB์ธ SQLite๋ฅผ ์ฌ์ฉ
- AWS์ ๋ฐฐํฌํ๊ฒ ๋๋ฉด์ 7.์ ์์ฑํ ์ด์ ๋ก DB ์๋ฒ๋ฅผ ๋ถ๋ฆฌํ๊ณ ์ถ์ด์ง
โ ํด๊ฒฐ: AWS RDS์ PostgreSQL๋ก DB ์๋ฒ๋ฅผ ์์ฑ, ORM์ ์ฑํํ ๋์ ์์ฃผ ์ฌ์ด ๋ง์ด๊ทธ๋ ์ด์ ์์ ์ด ๋์์
- SSL ์ธ์ฆ์๋ฅผ ๋ฐ๊ธ ๋ฐ์์ ๋ณด์์ ๊ฐํํ๊ณ HTTPS ์ ์์ด ๋์์ผ๋ฉด ํจ
โ ๋ฏธํด๊ฒฐ: ๋๋ฉ์ธ ๋ค์์ ๋ฐ๊ธ๋ฐ์ง ์์ผ๋ฉด Let's Encrypt์์ SSL ์ธ์ฆ์ ๋ฐ๊ธ ๋ถ๊ฐ๋ฅ
- requirements.txt๋ฅผ ํตํด ๊ฐ๋ฐ ํ๊ฒฝ๊ณผ ๋๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ค์นํ๊ณ , ์น ์๋ฒ์ WSGI๋ ์ ์์ ์ผ๋ก ๋์๊ฐ๊ณ ์๋๋ฐ ์๋์ด ๋์ง ์์
โ ํด๊ฒฐ: ์ด์ ํ๊ฒฝ์์ FLASK_DEBUG๋ฅผ True๋ก ๋ฐ๊พธ๊ธฐ ์ซ์ด ๋ก๊ทธ๋ฅผ txt๋ก ์ ์ฅํ๋๋ก ํจ, ์ด๋ฅผ ํตํด APP_CONFIG_FILE ์ธ์์ด ์๋ผ ์ค๋ฅ๊ฐ ๋๊ณ ์์์์ ์๊ฒ๋จ, ์ด์ ์๋ฒ์ alias ์ค์ ์ ํตํด ํด๊ฒฐ
- ์ด์์ ๋ชฉํ๋ ์ ์ธ๊ณ ๋ฆฌํ ์ผ ๋ธ๋๋ ๋ด ๊ฐ๊ฒฉ API ์ ์ฉ
- ์ ์ด์ ํ๋ก์ ํธ์ ๊ธฐํ ์๋๋ ๋ฆฌํ ์ผ ์๋น์ค(์ ์ธ๊ณ ๋ฑ) ๋ด ์ปค๋ฎค๋ํฐ ์ปค๋จธ์ค ๋ฐ์ ์ด์๊ณ , API๋ฅผ ์ฌ์ฉํ๋ฉด ์ค์๊ฐ์ผ๋ก ๊ฐ๊ฒฉ DB ์ ๋ฐ์ดํธ ๊ฐ๋ฅ
- ํ์ง๋ง ์ ์ธ๊ณ Open API๊ฐ ์ ์ธ๊ณ ํ๋ซํผ์ ์ ์ ํ ์ํ ํ๋งค์์๊ฒ๋ง ํ์ฉ๋์ด์ ์ฌ์ฉํ ์ ์์
- ์ด์ ๋ํ ๋์์
- ๊ณต๊ณต๋ฐ์ดํฐ API
- ์ฅ์ : ๊ธฐ์ ์ ์ผ๋ก API ๋์ ๊ธฐ๋ฅ ๊ตฌํ ๊ฐ๋ฅ
- ๋จ์ : '๋ ์ํผ'๋ผ๋ ๊ธฐํ ์๋๋ฅผ ์ถฉ์กฑ์ํค๊ธฐ์๋ ๋ฐ์ดํฐ๋ก ์ ๊ณต๋๋ ํ๋ชฉ์ด ์ ํ๋จ
- ์ด๋งํธ๋ชฐ(์ ์ธ๊ณ ๋ฆฌํ
์ผ ๋ธ๋๋)์์ ์ง์ ๊ฐ๊ฒฉ ์กฐ์ฌ
- ์ฅ์ : ๊ธฐํ ์ทจ์ง์ ๋ง๊ฒ '๋ ์ํผ'์ ์์ฃผ ๋ฑ์ฅํ๋ ์ฌ๋ฃ ์ ์ ๊ฐ๋ฅ
- ๋จ์ : ๋ฐ์ดํฐ์ ์ค์๊ฐ์ฑ, ์ ๋ขฐ์ฑ ์ด์
- ๊ณต๊ณต๋ฐ์ดํฐ API
- ํด๋น ๋์์ ์ด๋ป๊ฒ ์ ํํ ์ง์ ๋ํด์ ํ์ ๊ฐ์ ์๊ฒฌ ์ฐจ์ด๊ฐ ์์์
โ ํด๊ฒฐ: ๊ณต๊ณต API๋ฅผ ๋์ ํ๊ณ , ์์๋ก ์กฐ์ฌํ ๊ฐ๊ฒฉ ๋ฐ์ดํฐ๋ฅผ ํผํฉํ์ฌ DB ๊ตฌ์ฑ ๊ฐ ๋์์ ์ฅ๋จ์ ์ ์ํธ ๋ณด์
- ์ด๋ฒ ํ๋ก์ ํธ์์ ๋๋ ํ ๋ฆฌ๋ฅผ ์ดํดํ๋ ๊ฒ์ ์ฃผ์์ ์ ๋์ต๋๋ค. ํ์ ์ ์ฒด๊ฐ Flask ํ์ต์ ํ๋ ๊ณผ์ ์์ ๋ค๋ฅธ ์กฐ์์ ๋นํด ์ดํด์๋๊ฐ ํ์ ํ ๋๋ ธ์ต๋๋ค. ๊ธฐ๋ณธ์ ์ธ ๋๋ ํ ๋ฆฌ ๊ตฌ์ฑ์ ์ดํดํ์ง ๋ชปํ ๊ฒ์ด ์์ธ์ด์์ต๋๋ค. ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ด๋ฒ ํ๋ก์ ํธ์ ๋๋ ํ ๋ฆฌ ๊ตฌ์ฑ๊ณผ ๊ทธ ๋ป์ ๋ชจ๋ํฐ ํ ์ผ ์ ๋์๋๊ณ ๋ฐ๋ณต์ ์ผ๋ก ํ์ตํ์ต๋๋ค.
- ๊ฐ๋ฐ ๊ณผ์ ์ ๋๋ ท์ด ๊ธฐ์ฌํ ์ ์๋ ์ํฉ์ด์๊ธฐ์, ๋ฐฑ๋ฐ์ดํฐ ์์ง ๋ฑ ํ๋ก์ ํธ ๊ธฐํ ๋ถ๋ถ์ ์ฃผ๋์ ์ผ๋ก ์ํ์ต๋๋ค. ํนํ ์ด ๊ณผ์ ์์ โLean Startupโ ๊ณผ โAgileโ ๋ฐฉ์์ ๋ํด ๊ฐ๊ฐ์ ํน์ง๊ณผ ๋ ๊ฐ์ ์ฐจ์ด์ ์ ๋ช ํํ ์ดํดํ ์ ์์์ต๋๋ค. ์ด๋ฅผ ํฌํจํ์ฌ ์ ๊ฐ ํ์ตํ ๋ถ๋ถ์ ๋ ธ์ ๋ฌธ์๋ก ์์ฑํ์ฌ ํ์๋ค์๊ฒ ๊ณต์ ํ์์ต๋๋ค.
- ์ด๋ฒ ํ๋ก์ ํธ์ ๊ฐ์ฅ ํฐ ์ฑ๊ณผ๋ โ์ดํดํ ์ ์๋ READMEโ๋ฅผ ์ป์๋ค๋ ๊ฒ์ ๋๋ค. ๊ธฐ์ด๊ฐ ๋ถ์กฑํ์ฌ ๊นํ์ ์๋ ๋ค๋ฅธ ํ๋ก์ ํธ์ ๋ฆฌํฌํธ๋ค์ ํด์ํ๊ธฐ ์ด๋ ค์ ๋๋ฐ, ํ์ฅ์ธ ์น์๋์ด ์์ฑํด์ฃผ์ ์ต์ข ๋ฆฌํฌํธ๋ ๊ฐ๋ฐ ๊ณผ์ ์ ์๊ณ ์๊ธฐ์ ๋น ๋ฅด๊ฒ ์ดํดํ ์ ์์์ต๋๋ค. ํฅํ ์ด๋ฅผ ํ ๋๋ก ๋ค๋ฅธ ๊ฒ์ํ์ ์ง์ ๋ง๋ค์ด๋ณด๋ฉฐ ํ์ตํ ์์ ์ ๋๋ค. ํ ๋ด ์ปค๋ฎค๋์ผ์ด์ ์ด ์ํํ์ฌ, ํ ํฌ๋์ปฌ ์ง์์ด ๋ถ์กฑํ๋ฐ๋ ํ๋ก์ ํธ์ ๊ธฐ์ฌํ ์ ์์์ต๋๋ค. ํ์๋ค์๊ฒ ๊ฐ์ฌ๋ฅผ ํํฉ๋๋ค.
- Flask๋ฅผ ์ฌ์ฉํด์ ์น ๊ฐ๋ฐํด๋ณธ ์ ์ด ์์๋๋ฐ, ์ด๋ฒ ํ๋ก์ ํธ๋ฅผ ๊ฒฝํํจ์ผ๋ก์จ ์ข์ ๊ฒฝํ์ด ๋์๊ณ ORM ๊ธฐ๋ฅ์ ์ ์ฉํ๋ฉด DB ๋ง์ด๊ทธ๋ ์ด์ ๋ ๊ฐํธํ๊ณ DB๋ฅผ ๊ฐ์ฒดํ ํ์ ์ฌ์ฉํ๋ฉด์ ๋ฐ๋ก SQL๋ฌธ์ ์์ฑํ์ง ์์๋ ๋๋ ๊ฒ์ ํธ๋ฆฌํจ์ ๋๊ปด ์ด๋ฒ ํ๋ก์ ํธ์ ์ฌ์ฉ ๊ฒฝํ์ ํ ๋๋ก ์ถํ ๊ฐ๋ฐ์๋ ORM์ ๋ํ ๊ฐ๋ ์ ๋ณต์ต ํ์ ์ ์ฉํ๋ ค ํฉ๋๋ค. ๊ฐ๋ฐ ๊ณผ์ ์์ ๊ฒฝ๋ก ์ค์ ๋ฌธ์ ๋ DB ์ฐ๋ ๋ฌธ์ , ์ด๋ฏธ์ง ํ์ผ ์ก/์์ ๋ฑ ์๋ฌ๋ ๋ง์ด ๋ฐ์ํ์๋๋ฐ ์ต๋ํ ๊ด๋ จ ๋ฌธ์ ๊ฒ์์ ํตํด ํด๊ฒฐํ๊ฑฐ๋ ํ์๋ถ๋ค๊ณผ ๊ฐ์ฌ๋์ ๋์์ ๋ฐ์ ํด๊ฒฐ ํ์ต๋๋ค. ๊ฐ์ฅ ๋ฌธ์ ๋ฅผ ๊ฒช์๋ ํํธ์ธ ๋ ์ํผ ๊ฒ์๋ฌผ์์ ์ด๋ฏธ์ง๋ฅผ ์ ๋ก๋ํ๋ ๋ถ๋ถ์ด์๋๋ฐ, ์ด๋ฏธ์ง ์ ์ก์ด ์๋ฃ๋๋ฉด ์ ์ฅ ํ๋ ค๋ ์ด๋ฏธ์ง ํ์ผ์ ์ด๋ฆ๋ช ์ DB์ ๋ฃ๊ณ ๊ฐ๊ณต ์ฒ๋ฆฌ๊ณผ์ ์ ํํธ๋ฅผ ๊ฑฐ์ณ์ผ ํ๋๋ฐ ๊ฐ๊ณต ์ฒ๋ฆฌ๊ณผ์ ์ ๋ก์ง์ ์ดํดํ๊ธฐ ์ฝ๊ฒ ์ค๋ช ํด์ฃผ์ ์ ๋ง์ ๊ณต๋ถ๊ฐ ๋์์ต๋๋ค. ๊ฐ๋ฐ ๊ณผ์ ์ค ๋ชจ๋ฅด๊ฑฐ๋ ํท๊ฐ๋ฆฌ๋ ๋ถ๋ถ์ ์ถํ์ ์ ํ ์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ํตํด ํ์ต ํ ์ ๋ฆฌํ ์์ ์ ๋๋ค.
- html๊ณผ css์ ๋ํด์ ์ฌ๋ฌ๊ฐ์ง ๊ธฐ๋ฅ์ ์ค์ตํ๋ฉด์ ํ๋ก ํธ์ ๋ํ ์ดํด๋๊ฐ ๋์์ก์ต๋๋ค. ๊ธฐ์ด ์ฝ๋ ์์ฑ์ ํ์์ง๋ง ์ถ๊ฐ ๊ธฐ๋ฅ ๊ตฌํ์ ๋ํ ์ดํด๋๊ฐ ๋ถ์กฑํ๊ธฐ ๋๋ฌธ์ ์ถํ์๋ ๋ ๊ณต๋ถํ์ฌ ๋ฐฑ์๋ ๊ธฐ๋ฅ ๊ตฌํ์๋ ๋์ ํด๋ณด๊ณ ์ถ์ต๋๋ค.
- ์ฃผ๋ก Spring Boot๋ก ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ๋ค๊ฐ Flask๋ฅผ ์ฒ์ ์ฌ์ฉํด๋ดค์ต๋๋ค. ๊ฐ๋ฐ์ ์งํํ๋ฉฐ ํ๋ ์์ํฌ๊ฐ ๊ฐ๊ฒฐํ๊ณ ๊ฐ๋ฒผ์ ๋ณธ ํ๋ก์ ํธ์ ์ต์ ์ด๋ผ๋ ์๊ฐ์ ํ์ต๋๋ค. JPA๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝํ์ ํ ๋๋ก ๋ณธ ํ๋ก์ ํธ์๋ ORM์ ์ ์ฉํด๋ณด์๋๋ฐ, ์ญ์๋ ์ ์คํ์ผ์ ๋๋ค. ๊ฐ๋ฐ ์ค์๋ ๋ฌผ๋ก ์ด๊ณ , ํนํ ์ด๋ฐ ๊ฐ๋ฐ ํ๊ฒฝ์์ ์ฐ๋ Sqlite๋ฅผ ์ด์ ์๋ฒ์ ๋ฐฐํฌํ ๋ PostgreSQL๋ก ๋ง์ด๊ทธ๋ ์ด์ ํ๋๋ฐ, ๊ฐ๋์ ๋๋ฌผ์ ํ๋ ธ์ต๋๋ค. ๋ํ ์ด์ ์๋ฒ ๊ตฌ์ถ ๊ณผ์ ์์ ํ์์ WAS๊ฐ ๋ด์ฅ๋ Spring Boot๋ง ์ฐ๋ค๋ณด๋ Nginx ๊ฐ์ ์น ์๋ฒ๋, WSGI๋ ํ๋ ๋ฌธ์ ๋ ์ ํ ์ฒด๊ฐํ์ง ๋ชปํ๋๋ฐ, Flask๋ฅผ ์ฌ์ฉํ๋ฉด์ ์ด๋ฐ ์๋ฒ๋ฅผ ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ฐฐ์นํด๋ณด๋ฉฐ ๋ง์ด ๋ฐฐ์ ์ต๋๋ค. ์งง์ ๊ธฐ๊ฐ์ด์์ง๋ง ํ์๋ค๊ณผ ์ปค๋ฎค๋์ผ์ด์ ์ด ์์ฃผ ๋ง์กฑ์ค๋ฝ๊ณ , ๋๋ถ์ ํ ํฌ๋์ปฌํ ๋ถ๋ถ์ ์ง์คํ ์ ์์์ต๋๋ค. ์ฆ๊ฑฐ์ ์ต๋๋ค.
- Flask ํ๋ ์์ํฌ๋ฅผ ํ์ฉํ ํ๋ก์ ํธ๋ฅผ ํตํด ์ ๋ ๋ค์ํ ๊ฒ๋ค์ ๋ฐฐ์ฐ๊ณ ์ฑ์ฅํ ์ ์์์ต๋๋ค. ์ฑ , ๊ฐ์ ์๋ฃ, ๊ทธ๋ฆฌ๊ณ ํ์๋ค๊ณผ์ ์คํฐ๋๋ฅผ ํตํด Python, Flask, ๊ทธ๋ฆฌ๊ณ HTML์ ๊ธฐ๋ณธ ๊ฐ๋ ๊ณผ ๊ตฌ์กฐ๋ฅผ ์ต๋ํ๊ณ ์ค์ ๋ก ์ฝ๋๋ฅผ ์์ฑํ๋ฉฐ ์์ฉํ๋ ๊ณผ์ ์ ๋งค์ฐ ์ ์ตํ์ต๋๋ค. ํนํ, ์ฟผ๋ฆฌ๋ฌธ์ ์ฌ์ฉํ๋ ๋์ ORM์ ์ฌ์ฉํด๋ณด๋ฉด์ SQL ๋ฌธ๋ฒ์ ๋ฐฐ์ฐ์ง ์์๋ ์ง๊ด์ ์ผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ํธ์์ฉํ ์ ์๋ ์ฅ์ ์ ๊ฒฝํํ์ต๋๋ค. ORM์ ํ์ฉ์ผ๋ก ์ธํด ๋ณต์กํ SQL ๋ฌธ๋ฒ์ ๋ํ ์ด๋ ค์ ์์ด ๋ณด๋ค ๊ฐํธํ๊ฒ ๋ฐ์ดํฐ ์กฐ์ ๋ฐ ๊ด๋ฆฌ๊ฐ ๊ฐ๋ฅํด์ก์ต๋๋ค. ๊ทธ๋ฌ๋ ํ์ด์ฌ๊ณผ DB ๊ตฌ์กฐ์ ๋ํ ์ ๋ฐ์ ์ธ ์ดํด ๋ถ์กฑ์ผ๋ก ์ธํ์ฌ HTML ์ฌ์ง ์ ์ฅ ๊ธฐ๋ฅ์ ๊ตฌํํ ์ ์์์ผ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ๋ ๊ณผ์ ์์ ์ด๋ ค์์ ๊ฒช์์ต๋๋ค. ์ฐฝ์์ ์ธ ์์ด๋์ด๋ก ํ์๋ค์๊ฒ ์ธ์ ์ ๋ฐ์์ง๋ง ๊ธฐ๋ฅ์ ๊ตฌํํ์ง ๋ชปํ๋ ํ๊ณ๊ฐ ์ ์๊ฒ ๋๊ธฐ๋ถ์ฌ๊ฐ ๋์์ต๋๋ค. ์ง์์ ์ธ ๋ฆฌ๋ทฐ์ ์๊ธฐ๊ณ๋ฐ ๋ ธ๋ ฅ์ผ๋ก ๋ฅ๋ ฅ์ ํฅ์์ํค๋ฉฐ ๋ค์ํ ํ๋ก์ ํธ์ ๋์ ํ์ฌ ์์ ์์์๋ง ๊ทธ๋ ธ๋ ๊ฒ๋ค์ ํ์ค๋ก ๋ง๋ค์ด๋ด๋ ๊ฐ๋ฐ์๊ฐ ๋๊ฒ ์ต๋๋ค.