Skip to content

seungwonbased/ssg-recipe-project

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

65 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿฅ™ ํ•œ๋ผ์–ผ๋งˆ Report๐Ÿ’ฐ

1. ๐Ÿ‘จ๐Ÿฝโ€๐Ÿ’ป ํ•œ๋ผ์–ผ๋งˆ ์†Œ๊ฐœ

main_page

ํ•œ๋ผ์–ผ๋งˆ๋Š” ๋ ˆ์‹œํ”ผ๋ฅผ ๊ณต์œ ํ•˜๊ณ , ๋ ˆ์‹œํ”ผ์˜ ๊ฐ€๊ฒฉ์„ ์ œ๊ณตํ•˜๋Š” ์ปค๋ฎค๋‹ˆํ‹ฐ์ž…๋‹ˆ๋‹ค.

ํ•œ๋ผ์–ผ๋งˆ๋ฅผ ํ†ตํ•ด ๋ ˆ์‹œํ”ผ๋ฅผ ๊ณต์œ ํ•˜๊ณ , ๊ฐ€์„ฑ๋น„ ์žˆ๋Š” ํ•œ๋ผ ์ฑ™๊ธฐ์„ธ์š” ๐Ÿฅ˜

๐Ÿš€ ๋ฐฐํฌ URL

2. ๐Ÿ‘ฅ ํŒ€ ์†Œ๊ฐœ

๋ฐฐ์Šน์› ์„œ์ข…ํ›ˆ ์ด์ง€์œค ์ •์ง€ํ™˜ ๊น€์ง€ํ›ˆ
img img img img img
Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge

3. ๐Ÿ—“๏ธ ๊ฐœ๋ฐœ ๊ธฐ๊ฐ„ (23๋…„ 9์›” 1์ผ ~ 23๋…„ 9์›” 7์ผ)

๐Ÿ“Œ ์ผ๋ณ„ ํ”„๋กœ์ ํŠธ ๋งˆ์ผ์Šคํ†ค

์ผ๋ณ„ ๋‚ด์šฉ
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)
- ๋ณด๊ณ ์„œ ์ž‘์„ฑ

4. ๐ŸŒ ๊ฐœ๋ฐœ ๋ฐ ์šด์˜ ํ™˜๊ฒฝ

โš™๏ธ ๊ธฐ์ˆ  ์Šคํƒ

Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge Static Badge

โŒจ๏ธ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ

  • ์Šน์›
    • OS: Static Badge
    • Editor: Static Badge
  • ์ข…ํ›ˆ
    • OS: Static Badge
    • IDE: Static Badge

๐Ÿญ ์šด์˜ ํ™˜๊ฒฝ

  • Static Badge Lightsail Instance
    • 1GB RAM,ย 2ย vCPU,ย 40GBย SSD
  • OS: Static Badge
  • DB: Static Badge

โ˜Ž๏ธ Communication Tool: Discord

์ฑ„๋„ ๊ตฌ์„ฑ

- CHAT CHANNEL
	# ๋ผ์šด์ง€
	# ๊ณต์ง€
	# ์•„์ด๋””์–ด
	# ๋ ˆํผ๋Ÿฐ์Šค
	# ์ด์Šˆ
	# ์งˆ๋ฌธ
	# ํŒŒ์ผ-๋ฐ-์ฝ”๋“œ
- VOICE CHANNEL
	# ๋ฏธํŒ…
  • # ๋ผ์šด์ง€: ์ž์œ ๋กญ๊ฒŒ ํ† ๋ก 
  • # ๊ณต์ง€: ๋ฏธํŒ…, ์ผ์ •, ํšŒ์˜๋ก ๊ณต์ง€
  • # ์•„์ด๋””์–ด: ๋ฒˆ๋œฉ์ด๋Š” ์•„์ด๋””์–ด
  • # ๋ ˆํผ๋Ÿฐ์Šค: ์ฐธ๊ณ ํ• ๋งŒํ•œ ์„œ๋น„์Šค, ์ž๋ฃŒ ๋ ˆํผ๋Ÿฐ์Šค
  • # ์ด์Šˆ: ๊ฐœ๋ฐœ ๋ฐ ํ”„๋กœ์ ํŠธ ์ง„ํ–‰์— ์žˆ์–ด ์ƒ๊ธฐ๋Š” ์ด์Šˆ
  • # ์งˆ๋ฌธ: ์ง„์งœ ์•„๋ฌด ์งˆ๋ฌธ์ด๋‚˜
  • # ํŒŒ์ผ-๋ฐ-์ฝ”๋“œ: ํŒŒ์ผ ๋ฐ ์ฝ”๋“œ ๊ณต์œ 
  • # ๋ฏธํŒ…: ๋ณด์ด์Šค & ํŽ˜์ด์Šค ๋ฏธํŒ…

5. โš’๏ธ ์„œ๋น„์Šค ๊ธฐ๋Šฅ

5.1. URL ๊ตฌ์กฐ

/ : ๋ฉ”์ธ ํŽ˜์ด์ง€, 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 : ๋กœ๊ทธ์•„์›ƒ

5.2. ์„œ๋น„์Šค ๊ธฐ๋Šฅ

ํšŒ์› ๊ฐ€์ž…

  • ์‚ฌ์šฉ์ž๊ฐ€ ํšŒ์› ์ •๋ณด(์ด๋ฆ„, ์ด๋ฉ”์ผ, ๋น„๋ฐ€๋ฒˆํ˜ธ)๋ฅผ ์ž…๋ ฅํ•˜์—ฌ ์ƒˆ๋กœ์šด ๊ณ„์ •์„ ์ƒ์„ฑ
  • ์ž…๋ ฅ๋œ ๋น„๋ฐ€๋ฒˆํ˜ธ๋Š” ํ•ด์‹œ๋กœ ์•”ํ˜ธํ™”๋˜์–ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋จ

๋กœ๊ทธ์ธ

  • ๋“ฑ๋ก๋œ ์‚ฌ์šฉ์ž๊ฐ€ ์ž์‹ ์˜ ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธํ•  ์ˆ˜ ์žˆ์Œ
  • ์ธ์ฆ์„ ์œ„ํ•ด ์‚ฌ์šฉ์ž ์ด๋ฆ„๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ํ™•์ธํ•˜๊ณ  ์„ธ์…˜์„ ์œ ์ง€

๋ ˆ์‹œํ”ผ ์ž‘์„ฑ

  • ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๊ฐ€ ๋ ˆ์‹œํ”ผ๋ฅผ ๊ฒŒ์‹œ๊ธ€ ํ˜•ํƒœ๋กœ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ์Œ
  • ์š”๋ฆฌ ์ด๋ฆ„, ๋ ˆ์‹œํ”ผ ๋“ฑ์˜ ํ…์ŠคํŠธ ํ•„๋“œ๋ฅผ ์ž…๋ ฅํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋จ
  • ํ•˜๋‹จ์˜ ์…€๋ ‰ํŠธ ๋ฐ•์Šค ํ˜•์‹์œผ๋กœ ์žฌ๋ฃŒ๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๊ณ , ์ค‘๋Ÿ‰ ๋˜๋Š” ์ˆ˜๋Ÿ‰์„ ์ž…๋ ฅํ•˜๋ฉด ๊ฐ€๊ฒฉ์ด ๊ณ„์‚ฐ๋จ
  • ๋งŒ์•ฝ ๋กœ๊ทธ์ธ ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ๋กœ๊ทธ์ธ ์ฐฝ์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ

๋ ˆ์‹œํ”ผ ์กฐํšŒ

  • ์ž‘์„ฑํ•œ ๋ ˆ์‹œํ”ผ๋Š” ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž, ๋กœ๊ทธ์ธํ•˜์ง€ ์•Š์€ ์‚ฌ์šฉ์ž ๋ชจ๋‘๊ฐ€ ๋ณผ ์ˆ˜ ์žˆ์Œ
  • ๊ณ„์‚ฐ๋œ ๊ฐ€๊ฒฉ์ด ๋ ˆ์‹œํ”ผ ํ•˜๋‹จ์— ์ถœ๋ ฅ๋จ

๋ ˆ์‹œํ”ผ ์ˆ˜์ •

  • ๋ ˆ์‹œํ”ผ ๊ณต์œ ์ž ๋˜๋Š” ๊ด€๋ฆฌ์ž ๊ถŒํ•œ์„ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž๋งŒ ํ•ด๋‹น ๋ ˆ์‹œํ”ผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Œ
  • ์„ ํƒ๋œ ๋ ˆ์‹œํ”ผ์˜ ํ•„๋“œ๋“ค(์š”๋ฆฌ ์ด๋ฆ„, ๋ ˆ์‹œํ”ผ, ์‚ฌ์ง„, ์‹ํ’ˆ ์นดํ…Œ๊ณ ๋ฆฌ, ๊ฐ€๊ฒฉ)์„ ์ˆ˜์ •ํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์—…๋ฐ์ดํŠธ

๋ ˆ์‹œํ”ผ ์‚ญ์ œ

  • ๋ ˆ์‹œํ”ผ ๊ณต์œ ์ž ๋˜๋Š” ๊ด€๋ฆฌ์ž ๊ถŒํ•œ์„ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž๋งŒ ํ•ด๋‹น ๋ ˆ์‹œํ”ผ๋ฅผ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Œ
  • ์„ ํƒ๋œ ๋ ˆ์‹œํ”ผ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์‚ญ์ œ๋จ

๋ ˆ์‹œํ”ผ ์ข‹์•„์š”

  • ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๋Š” ๋ ˆ์‹œํ”ผ์— ์ข‹์•„์š”๋ฅผ ๋ˆ„๋ฅผ ์ˆ˜ ์žˆ์Œ
  • ์ž์‹ ์˜ ๋ ˆ์‹œํ”ผ๋ฅผ ์ข‹์•„์š”ํ•  ๊ฒฝ์šฐ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๋ฉ”์„ธ์ง€๊ฐ€ ์ถœ๋ ฅ๋จ

๋ ˆ์‹œํ”ผ ๋ฆฌ์ŠคํŠธ ์กฐํšŒ

  • ์ž‘์„ฑ๋œ ๋ ˆ์‹œํ”ผ๊ฐ€ 10๊ฐœ ๋‹จ์œ„๋กœ ํŽ˜์ด์ง€๋กœ ๋‚˜๋‰˜์–ด ๋ Œ๋”๋ง๋จ
  • ํ•œ ํŽ˜์ด์ง€์— ๋ณด์—ฌ์ง€๋Š” ๋ ˆ์‹œํ”ผ ๊ฐœ์ˆ˜๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๊ณ , ์ด์ „ / ๋‹ค์Œ ํŽ˜์ด์ง€ ๋งํฌ ๋˜๋Š” ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ๋ฅผ ํ†ตํ•ด ํŽ˜์ด์ง€๋ฅผ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ์Œ

๊ฒ€์ƒ‰

  • ๊ฒ€์ƒ‰ ์ฐฝ์—์„œ ์ „๋‹ฌ ๋ฐ›์€ ๋ฐ์ดํ„ฐ๊ฐ€ ์š”๋ฆฌ ์ด๋ฆ„, ๋ ˆ์‹œํ”ผ ๋‚ด์šฉ, ๋ ˆ์‹œํ”ผ ๊ณต์œ ์ž, ๋Œ“๊ธ€ ๋‚ด์šฉ, ๋Œ“๊ธ€ ์ž‘์„ฑ์ž ํ•ญ๋ชฉ์—์„œ OR ์กฐ๊ฑด์œผ๋กœ ๊ฒ€์ƒ‰ํ•œ ์งˆ๋ฌธ์„ ๋ฐ˜ํ™˜
  • ๋ฐ˜ํ™˜๋œ ๋ฐ์ดํ„ฐ๋Š” ๋ฆฌ์ŠคํŠธ ์กฐํšŒ์™€ ๊ฐ™์ด 10๊ฐœ ๋‹จ์œ„๋กœ ๋‚˜๋‰˜์–ด ๋ Œ๋”๋ง๋จ
  • ํ•œ ํŽ˜์ด์ง€์— ๋ณด์—ฌ์ง€๋Š” ๋ ˆ์‹œํ”ผ ๊ฐœ์ˆ˜๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๊ณ , ์ด์ „ / ๋‹ค์Œ ํŽ˜์ด์ง€ ๋งํฌ ๋˜๋Š” ํŽ˜์ด์ง€ ๋ฒˆํ˜ธ๋ฅผ ํ†ตํ•ด ํŽ˜์ด์ง€๋ฅผ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ์Œ

๋Œ“๊ธ€ ์ž‘์„ฑ

  • ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๋Š” ๋ ˆ์‹œํ”ผ์— ๋Œ“๊ธ€์„ ๋‹ฌ ์ˆ˜ ์žˆ์Œ
  • ๋กœ๊ทธ์ธํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด ๋Œ“๊ธ€ ์ฐฝ์ด ๋น„ํ™œ์„ฑํ™”๋˜์–ด ๋Œ“๊ธ€์„ ๋‹ฌ ์ˆ˜ ์—†์Œ
  • ๋Œ“๊ธ€์˜ ํ…์ŠคํŠธ ํ•„๋“œ๊ฐ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋จ

๋Œ“๊ธ€ ์กฐํšŒ

  • ๋Œ“๊ธ€์€ ๋ ˆ์‹œํ”ผ ํ•˜๋‹จ์—์„œ ๋ฆฌ์ŠคํŠธ๋กœ ์กฐํšŒํ•  ์ˆ˜ ์žˆ์Œ

๋Œ“๊ธ€ ์ˆ˜์ •

  • ๋Œ“๊ธ€ ์ž‘์„ฑ์ž ๋˜๋Š” ๊ด€๋ฆฌ์ž ๊ถŒํ•œ์„ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž๋งŒ ํ•ด๋‹น ๋Œ“๊ธ€์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Œ

๋Œ“๊ธ€ ์‚ญ์ œ

  • ๋Œ“๊ธ€ ์ž‘์„ฑ์ž ๋˜๋Š” ๊ด€๋ฆฌ์ž ๊ถŒํ•œ์„ ๊ฐ€์ง„ ์‚ฌ์šฉ์ž๋งŒ ํ•ด๋‹น ๋Œ“๊ธ€์„ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Œ

๋Œ“๊ธ€ ์ข‹์•„์š”

  • ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๋Š” ๋Œ“๊ธ€์— ์ข‹์•„์š”๋ฅผ ๋ˆ„๋ฅผ ์ˆ˜ ์žˆ์Œ
  • ์ž์‹ ์˜ ๋Œ“๊ธ€์„ ์ข‹์•„์š”ํ•  ๊ฒฝ์šฐ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๋ฉ”์„ธ์ง€๊ฐ€ ์ถœ๋ ฅ๋จ

6. ๐Ÿ“š ์„œ๋น„์Šค ํŠน์ง• ๋ฐ ์‚ฌ์šฉ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

6.1. ๋””๋ ‰ํ„ฐ๋ฆฌ ๊ตฌ์กฐ

๐Ÿ“ 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

๐Ÿ“ app

๐Ÿ’ก: Flask ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๊ด€๋ จ๋œ ๋””๋ ‰ํ„ฐ๋ฆฌ

  • ๐Ÿ“ api: ๊ณต๊ณต๋ฐ์ดํ„ฐ API๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ
  • ๐Ÿ“ static: ์ด๋ฏธ์ง€, css, js ๋“ฑ์˜ ์ •์  ํŒŒ์ผ์„ ์ €์žฅํ•˜๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ
  • ๐Ÿ“ templates: HTML ํ…œํ”Œ๋ฆฟ์„ ์ €์žฅํ•˜๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ
  • ๐Ÿ“ views: Blueprint ํŒŒ์ผ์„ ์ €์žฅํ•˜๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ
  • ๐Ÿ“„ __init__.py: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํŒฉํ† ๋ฆฌ ํ•จ์ˆ˜๊ฐ€ ์žˆ๋Š” ํŒŒ์ผ
  • ๐Ÿ“„ forms.py: ๋žœ๋”๋ง์— ํ•„์š”ํ•œ HTML ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•˜๋ฉฐ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ž…๋ ฅ ํผ์„ ์ •์˜ํ•˜๊ณ  ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” Form ํด๋ž˜์Šค๊ฐ€ ์ •์˜๋˜์–ด ์žˆ๋Š” ํŒŒ์ผ
  • ๐Ÿ“„ models.py: ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ์ฒด์— ๋‹ด์•„ ORM์„ ์ด์šฉํ•˜๊ธฐ ์œ„ํ•œ ํด๋ž˜์Šค๊ฐ€ ์ •์˜๋˜์–ด ์žˆ๋Š” ํŒŒ์ผ

๐Ÿ“ config

๐Ÿ’ก: ํ™˜๊ฒฝ ํŒŒ์ผ์„ ์ €์žฅํ•˜๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ

  • ๐Ÿ“„ __init__.py: ํŒจํ‚ค์ง€๋กœ ์ธ์‹ํ•˜๊ธฐ ์œ„ํ•ด ์ƒ์„ฑํ•œ ํŒŒ์ผ
  • ๐Ÿ“„ default.py: ๋ชจ๋“  ํ™˜๊ฒฝ์—์„œ ๊ณตํ†ต์œผ๋กœ ์‚ฌ์šฉํ•  ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์ €์žฅํ•˜๋Š” ํŒŒ์ผ
  • ๐Ÿ“„ develoment.py: ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉํ•  ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์ €์žฅํ•˜๋Š” ํŒŒ์ผ
  • ๐Ÿ“„ production.py: ์šด์˜ ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉํ•  ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์ €์žฅํ•˜๋Š” ํŒŒ์ผ

๐Ÿ“ logs

๐Ÿ’ก: ๋กœ๊ทธ ํŒŒ์ผ์„ ์ €์žฅํ•˜๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ, ์šด์˜ ํ™˜๊ฒฝ์—์„œ๋งŒ ์‚ฌ์šฉ

๐Ÿ“„ requirements.txt

๐Ÿ’ก: ๊ฐ€์ƒ ํ™˜๊ฒฝ์—์„œ ์„ค์น˜ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์„ ํŒŒ์ผ๋กœ ๋‚ด๋ณด๋‚ด ๋‹ค๋ฅธ ํ™˜๊ฒฝ์—์„œ๋„ ์‰ฝ๊ฒŒ ๊ฐ™์€ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•จ

6.2. Database Schema

img

  • ๋ณด๋ผ์ƒ‰ ํ…Œ์ด๋ธ”์€ Many-to-Many ๊ด€๊ณ„๋ฅผ ์œ„ํ•ด ์ƒ์„ฑํ•œ ํ…Œ์ด๋ธ”

6.3. Application Factory Pattern

# 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์„ ์‚ฌ์šฉํ•ด ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ  ์„ค์ •
      • ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์„œ๋กœ ๋‹ค๋ฅธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค๊ณ  ๋‹ค๋ฅธ ํ™˜๊ฒฝ(๊ฐœ๋ฐœ, ํ…Œ์ŠคํŠธ ํ”„๋กœ๋•์…˜)์—์„œ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Œ
    • ์ด๋ฅผ ํ†ตํ•ด ๋ณธ ํ”„๋กœ์ ํŠธ์—์„œ ๊ฐœ๋ฐœ๊ณผ ์šด์˜ ํ™˜๊ฒฝ์˜ ์„ค์ •์„ ๋‹ค๋ฅด๊ฒŒ ํ•จ
    • ์žฅ์ 
      • ํ™•์žฅ์„ฑ ํ–ฅ์ƒ
      • ์„ค์ •๊ณผ ํ™˜๊ฒฝ ๊ด€๋ฆฌ์˜ ์šฉ์ด

6.4. Blueprint

  • Flask ๋ธ”๋ฃจํ”„๋ฆฐํŠธ๋Š” ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋…๋ฆฝ์ ์ธ ๋ชจ๋“ˆ๋กœ ๋‚˜๋ˆ„๊ณ , ๊ฐ ๋ชจ๋“ˆ์„ ๊ฐœ๋ณ„์ ์œผ๋กœ ์ž‘์—…ํ•˜๊ฑฐ๋‚˜ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์คŒ
    • ๋ผ์šฐํŒ… ํ•จ์ˆ˜์˜ ์ง‘ํ•ฉ
  • ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฐœ๋ฐœํ•  ๋•Œ ์ฝ”๋“œ๋ฅผ ๋” ๊ตฌ์กฐํ™”ํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ฌ
  • ์žฅ์ 
    1. ๋ชจ๋“ˆํ™”
      • ๊ฐ ๊ธฐ๋Šฅ์„ ๋…๋ฆฝ์ ์ธ ๋ธ”๋ฃจํ”„๋ฆฐํŠธ๋กœ ์ •์˜ํ•˜์—ฌ ๋ชจ๋“ˆํ™”๋œ ์ฝ”๋“œ ์ž‘์„ฑ์ด ๊ฐ€๋Šฅํ•˜๋ฏ€๋กœ ์œ ์ง€ ๋ณด์ˆ˜์™€ ํ™•์žฅ์ด ์šฉ์ด
    2. ๋ผ์šฐํŒ… ๋ถ„๋ฆฌ
      • URL ๋ผ์šฐํŒ…์„ ๊ฐ ๋ธ”๋ฃจํ”„๋ฆฐํŠธ ๋‚ด์—์„œ ๊ด€๋ฆฌํ•˜๋ฏ€๋กœ ์ถฉ๋Œ์„ ๋ฐฉ์ง€ํ•˜๊ณ  URL ๊ตฌ์กฐ๋ฅผ ๊ตฌ์„ฑํ•˜๊ธฐ ์‰ฌ์›€
    3. ์ฝ”๋“œ ์žฌ์‚ฌ์šฉ
      • ๋น„์Šทํ•œ ๊ธฐ๋Šฅ์„ ๋‹ค๋ฅธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ
  • ๋ณธ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ชฉ์ ์— ๋”ฐ๋ผ ๋ชจ๋“ˆ๋กœ ๋ถ„ํ• ํ•ด ๋ผ์šฐํŒ… ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ–ˆ์Œ

6.4.1. @bp.route

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋ธ”๋ฃจํ”„๋ฆฐํŠธ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด URL ๊ฒฝ๋กœ์— ๋”ฐ๋ผ ์–ด๋–ค ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋  ์ง€๋ฅผ ์ •์˜ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ
  • @app.route ๋˜ํ•œ URL ๊ฒฝ๋กœ์™€ ํ•จ์ˆ˜ ๊ฐ„์˜ ๋งคํ•‘์„ ์ •์˜ํ•˜์ง€๋งŒ, @app.route๋Š” Flask ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐ์ฒด('app')์— ์ง์ ‘ ๋ผ์šฐํŒ… ํ•จ์ˆ˜๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๋ฐ˜๋ฉด, @bp.route ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ๋ธ”๋ฃจํ”„๋ฆฐํŠธ ๊ฐ์ฒด์— ๋ผ์šฐํŒ… ํ•จ์ˆ˜๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋จ
    • ๋ธ”๋ฃจํ”„๋ฆฐํŠธ๋Š” Flask ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ชจ๋“ˆํ™”ํ•˜๊ณ  ๋ผ์šฐํŒ…์„ ๊ตฌ์กฐํ™”ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋จ
    • ๋ธ”๋ฃจํ”„๋ฆฐํŠธ๋ฅผ ์‚ฌ์šฉํ•œ ๋ผ์šฐํŒ… ํ•จ์ˆ˜๋“ค์„ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ ์œ ์šฉํ•˜๋ฉฐ ๋ชจ๋“ˆํ™”, ํ™•์žฅ ๊ฐ€๋Šฅ์˜ ์žฅ์ ์ด ์žˆ์Œ

6.5. Server-Side Rendering

  • Front-end ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐœ๋ฐœ์ด ๋ถˆ๊ฐ€๋Šฅํ•ด ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ทฐ๋ฅผ ์„œ๋ฒ„์—์„œ ์ƒ์„ฑํ•ด ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹์ธ SSR ์‚ฌ์šฉ

6.6. Object-Relational Mapping

  • ๊ฐ์ฒด์™€ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๊ฐ„์˜ ์ƒํ˜ธ ์ž‘์šฉ์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋งŒ๋“ค์–ด์ฃผ๋Š” ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ธฐ์ˆ  ๋˜๋Š” ๋„๊ตฌ
  • ORM์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”๊ณผ OOP ์–ธ์–ด ๊ฐ„์˜ ๋ถˆ์ผ์น˜๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ ๊ฐ„์˜ ์ƒํ˜ธ ์ž‘์šฉ์„ ์ถ”์ƒํ™”
  • Python์—์„œ๋Š” SQLAlchemy ์‚ฌ์šฉ
  • ์žฅ์ 
    1. ๊ฐ์ฒด์ง€ํ–ฅ ์ฝ”๋“œ๋กœ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ์ง‘์ค‘ ๊ฐ€๋Šฅ
    2. ์žฌ์‚ฌ์šฉ ๋ฐ ์œ ์ง€๋ณด์ˆ˜ ํŽธ๋ฆฌ์„ฑ ์ฆ๊ฐ€
    3. DBMS์— ๋Œ€ํ•œ ์ข…์†์„ฑ ์ค„์–ด๋“ฌ
  • ๋‹จ์ 
    1. ORM๋งŒ์œผ๋กœ๋Š” ์„œ๋น„์Šค ๊ตฌํ˜„์ด ์–ด๋ ค์›€
    2. ํ”„๋กœ์‹œ์ €(์ž‘์—…)๊ฐ€ ๋งŽ์€ ์‹œ์Šคํ…œ์—์„œ๋Š” ์žฅ์ ์„ ์ทจํ•˜๊ธฐ ์–ด๋ ค์›€
  • Workflow
    1. ๋ฐ์ดํ„ฐ ๋ชจ๋ธ ์ •์˜: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”๊ณผ ๋งคํ•‘๋  ๋ฐ์ดํ„ฐ ๋ชจ๋ธ(ํด๋ž˜์Šค)์„ ์ •์˜
    2. ORM ์„ค์ •: ORM์„ ์„ค์ •ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ์ •๋ณด์™€ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ ๊ฐ„์˜ ๋งคํ•‘ ๊ทœ์น™์„ ์„ค์ •
    3. CRUD ์ž‘์—…: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ์—์„œ ORM์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ CRUD
    4. ์ฟผ๋ฆฌ ์‹คํ–‰: ORM์€ ๊ฐœ๋ฐœ์ž๊ฐ€ ์ž‘์„ฑํ•œ ORM ์ฝ”๋“œ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ SQL ์ฟผ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ „์†กํ•˜์—ฌ ์‹คํ–‰
    5. ๊ฒฐ๊ณผ ์ฒ˜๋ฆฌ: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋ฐ˜ํ™˜๋œ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜ ํ•„์š”ํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰
  • ์˜ˆ์‹œ: 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';

6.6.1. Flask ORM Library: SQLAlchemy

  • Python์„ ์œ„ํ•œ ORM ๋ฐ SQL ํˆดํ‚ท ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
  • ๋ฐ์ดํ„ฐ๋ฐ์ด์Šค์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๊ธฐ ์œ„ํ•œ ๋„๊ตฌ ์ œ๊ณต
  • ๊ตฌ์„ฑ ์š”์†Œ
    • Core: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋ณธ ๋„๊ตฌ ์ œ๊ณต
    • ORM: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ Python ํด๋ž˜์Šค ์‚ฌ์ด์˜ ๋งคํ•‘ ์ œ๊ณต
    • SQLAlchemy: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”์„ ํŒŒ์ด์ฌ ํด๋ž˜์Šค๋กœ ์ •์˜
    • Session: ์„ธ์…˜์„ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋Œ€ํ•œ ํŠธ๋žœ์žญ์…˜์„ ๊ด€๋ฆฌ
    • Engine: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋Œ€ํ•œ ๋ชจ๋“  SQL ์ž‘์—…์ด ์ˆ˜ํ–‰๋จ
    • Query: SQL ์ฟผ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์‹คํ–‰ํ•˜๋Š” ์ฟผ๋ฆฌ ๋นŒ๋”๋ฅผ ์ œ๊ณต
  • DB ๊ด€๋ฆฌ ๋ช…๋ น์–ด
    • flask db migrate: ๋ชจ๋ธ์„ ์ƒ์„ฑํ•˜๊ฑฐ๋‚˜ ๋ณ€๊ฒฝํ•  ๋•Œ ์‚ฌ์šฉ
    • flask db upgrade: ๋ชจ๋ธ์˜ ๋ณ€๊ฒฝ ๋‚ด์šฉ์„ ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ ์šฉํ•  ๋•Œ ์‚ฌ์šฉ

6.6.2. Model

  • Models.py ํŒŒ์ผ์— ORM์„ ์‚ฌ์šฉํ•ด ๋ชจ๋ธ์„ ์ •์˜ํ•จ
    • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ํ…Œ์ด๋ธ”๊ณผ ์ƒํ˜ธ์ž‘์šฉํ•˜๊ธฐ ์œ„ํ•œ ํด๋ž˜์Šค๋ฅผ ์ •์˜
  • ํด๋ž˜์Šค์˜ ๊ฐ ์†์„ฑ์€ ๋งคํ•‘๋˜๋Š” ํ…Œ์ด๋ธ”์˜ Column๊ณผ ์ผ์น˜
  • ๋ฐ์ดํ„ฐ ์œ ํ˜•๊ณผ ์ œ์•ฝ ์กฐ๊ฑด ๋˜ํ•œ ์ง€์ •

6.7. Open API

  • ์žฌ๋ฃŒ ํ…Œ์ด๋ธ”์˜ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ 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()

6.7.1. ๋ ˆ์‹œํ”ผ ๊ฐ€๊ฒฉ ๊ณ„์‚ฐ ๋กœ์ง

  • Open API์—์„œ ๋ฐ›์•„์˜จ ๋ฐ์ดํ„ฐ์™€ ์ด๋งˆํŠธ๋ชฐ์—์„œ ์กฐ์‚ฌํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ํ˜ผํ•ฉํ•ด DB์— ์‚ฝ์ž…ํ•˜๊ณ , ๋ ˆ์‹œํ”ผ ์ž‘์„ฑ ์š”์ฒญ์ด ๋“ค์–ด์˜ค๋ฉด ์žฌ๋ฃŒ ํ…Œ์ด๋ธ”์˜ ์ •๋ณด๋ฅผ ํ…œํ”Œ๋ฆฟ ์—”์ง„์œผ๋กœ ๋„˜๊น€
  • ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ์žฌ๋ฃŒ์™€ ์ˆ˜๋Ÿ‰ ํ˜น์€ ์ค‘๋Ÿ‰์ด Flask๋กœ ๋„˜์–ด์˜ค๋ฉด ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ํ›„ ๊ฐ€๊ฒฉ์„ ๊ณ„์‚ฐํ•˜๋Š” ํ•จ์ˆ˜์—์„œ ์ฒ˜๋ฆฌ๋จ

๋ ˆ์‹œํ”ผ ๊ฐ€๊ฒฉ = (์žฌ๋ฃŒ ๊ฐ€๊ฒฉ * ์ˆ˜๋Ÿ‰ ํ˜น์€ ์ค‘๋Ÿ‰ / ๋‹จ์œ„ ) + (์žฌ๋ฃŒ ๊ฐ€๊ฒฉ * ์ˆ˜๋Ÿ‰ ํ˜น์€ ์ค‘๋Ÿ‰ / ๋‹จ์œ„ ) + ...

6.8. HTTP ์š”์ฒญ - request

  • Flask ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ HTTP ์š”์ฒญ๊ณผ ๊ด€๋ จ๋œ ์ •๋ณด๋ฅผ ์ฒ˜๋ฆฌํ•˜๋ ค๋ฉด 'request' ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉ
  • ์ด ๊ฐ์ฒด๋Š” ํ˜„์žฌ ์š”์ฒญ๊ณผ ๊ด€๋ จ๋œ ๋‹ค์–‘ํ•œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜๋ฉฐ HTTP ๋ฉ”์„œ๋“œ, URL, ํ—ค๋”, ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด, ํผ ๋ฐ์ดํ„ฐ, JSON ๋ฐ์ดํ„ฐ ๋ฐ ๊ธฐํƒ€ ์š”์ฒญ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์คŒ
  • ๋ณธ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” Blueprint, API ํ˜ธ์ถœ ๋ชจ๋“ˆ์—์„œ ์ฃผ๋กœ ์‚ฌ์šฉ
  • 'request' ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด ์–ป์„ ์ˆ˜ ์žˆ๋Š” ์ฃผ์š” ์ •๋ณด
    1. HTTP ๋ฉ”์†Œ๋“œ: request.method๋ฅผ ํ†ตํ•ด ํ˜„์žฌ ์š”์ฒญ์˜ HTTP ๋ฉ”์†Œ๋“œ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Œ
      • ์˜ˆ๋ฅผ ๋“ค์–ด, GET, POST, PUT, DELETE ๋“ฑ์˜ ๋ฉ”์†Œ๋“œ๊ฐ€ ๋ฐ˜ํ™˜๋จ
    2. URL ์ •๋ณด: request.url์€ ํ˜„์žฌ ์š”์ฒญ์˜ ์ „์ฒด URL์„ ๋‚˜ํƒ€๋ƒ„
    3. ํ—ค๋” ์ •๋ณด: request.headers๋Š” HTTP ์š”์ฒญ ํ—ค๋”์˜ ๋”•์…”๋„ˆ๋ฆฌ๋ฅผ ๋ฐ˜ํ™˜
      • ์˜ˆ๋ฅผ ๋“ค์–ด, request.headers['User-Agent']๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž ์—์ด์ „ํŠธ(User-Agent) ํ—ค๋”๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Œ
    4. ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด: request.args๋Š” URL์˜ ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋”•์…”๋„ˆ๋ฆฌ๋กœ ๋ฐ˜ํ™˜
      • ์˜ˆ๋ฅผ ๋“ค์–ด, /search?query=example URL์—์„œ request.args['query']๋Š” "example" ๊ฐ’์„ ๋ฐ˜ํ™˜
    5. ํผ ๋ฐ์ดํ„ฐ: request.form์€ POST ์š”์ฒญ์˜ ํผ ๋ฐ์ดํ„ฐ๋ฅผ ๋”•์…”๋„ˆ๋ฆฌ๋กœ ๋ฐ˜ํ™˜, HTML ํผ์—์„œ ์ œ์ถœ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•  ๋•Œ ์œ ์šฉ
    6. JSON ๋ฐ์ดํ„ฐ: request.get_json()์„ ์‚ฌ์šฉํ•˜์—ฌ JSON ์š”์ฒญ ๋ณธ๋ฌธ์„ ํŒŒ์‹ฑํ•˜๊ณ  JSON ๋ฐ์ดํ„ฐ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Œ
    7. ํŒŒ์ผ ์—…๋กœ๋“œ: ํŒŒ์ผ ์—…๋กœ๋“œ๊ฐ€ ์žˆ๋Š” POST ์š”์ฒญ์˜ ๊ฒฝ์šฐ, request.files๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์—…๋กœ๋“œ๋œ ํŒŒ์ผ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Œ
    8. ์„ธ์…˜ ์ •๋ณด: request.session์„ ์‚ฌ์šฉํ•˜์—ฌ ํ˜„์žฌ ์š”์ฒญ๊ณผ ๊ด€๋ จ๋œ ์„ธ์…˜ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Œ

6.9. Flask Form

  • Flask ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์›น ํผ์„ ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ํ™•์žฅ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
  • ์›น ํผ์„ ์‰ฝ๊ฒŒ ์ƒ์„ฑํ•˜๊ณ , Form validation, ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘, ์—๋Ÿฌ ์ฒ˜๋ฆฌ ๋“ฑ์„ ํ•  ์ˆ˜ ์žˆ์Œ
  • CSRF(Cross-Site Request Forgery) ๋ฐฉ์ง€ ๊ฐ™์€ ๋ณด์•ˆ ๊ธฐ๋Šฅ๋„ ๋‚ด์žฅ๋˜์–ด ์žˆ์Œ
    • CSRF ํ† ํฐ ์ฝ”๋“œ๋ฅผ ์‚ฝ์ž…ํ•ด์•ผ ์ •์ƒ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ ์ „์†ก ๊ฐ€๋Šฅ
  • ์‚ฌ์šฉ ๋ฐฉ๋ฒ•
    1. FlaskForm์„ ์ƒ์† ๋ฐ›๋Š” Form ํด๋ž˜์Šค๋ฅผ ์ •์˜
    2. View์˜ ๋ผ์šฐํŒ… ํ•จ์ˆ˜์—์„œ Form ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์‚ฌ์šฉ
      • POST ์š”์ฒญ์ผ ๋•Œ ๊ฒ€์ฆ ์ž‘์—… ๋“ฑ
    3. HTML ํ…œํ”Œ๋ฆฟ ํŒŒ์ผ์—์„œ ์›น ํผ์„ ๋ Œ๋”๋งํ•˜๊ณ  ์‚ฌ์šฉ์ž์—๊ฒŒ ์ž…๋ ฅ์„ ๋ฐ›์Œ

6.10. ํŽ˜์ด์ง• - Pagenation

  • 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

6.11. ํšŒ์›๊ฐ€์ž… - WTForms

  • ์›น์—์„œ ํผ์„ ์ •์˜ํ•˜๊ณ  ๊ฒ€์ฆํ•˜๊ธฐ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
    • ์›น ํผ์„ ์‰ฝ๊ฒŒ ์ƒ์„ฑํ•˜๊ณ , ์‚ฌ์šฉ์ž๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ  ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ์Œ
  • ํšŒ์›๊ฐ€์ž…์„ ์œ„ํ•ด FlaskForm์„ ์ƒ์† ๋ฐ›์•„ UserCreateForm ์ƒ์„ฑ
  • PasswordField, EmailField์„ import
    • PasswordField: ์‚ฌ์šฉ์ž์˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅ๋ฐ›์„ ๋•Œ ์ด ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ์„ ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ณ  Validation๋„ ํ•  ์ˆ˜ ์žˆ์Œ
      • ๋ผ์šฐํŒ… ํ•จ์ˆ˜์—์„œ generate_password_hash ํ•จ์ˆ˜๋กœ ์•”ํ˜ธํ™”
    • EmailField: StringField์™€ ๋™์ผํ•˜์ง€๋งŒ ํ…œํ”Œ๋ฆฟ ์ž๋™ ๋ณ€ํ™˜์œผ๋กœ ์‚ฌ์šฉ์‹œ <input type="email">๋กœ ๋ณ€ํ™˜๋จ

6.12. ๋กœ๊ทธ์ธ - session, g

  • FlaskForm์„ ์ƒ์† ๋ฐ›์•„ UserLoginForm ์ƒ์„ฑ
  • ๋กœ๊ทธ์ธ ์—ฌ๋ถ€๋Š” g ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด ๋ผ์šฐํŒ… ํ•จ์ˆ˜์—์„œ ๊ฒ€์ฆ
  • ๋กœ๊ทธ์ธ ๋ผ์šฐํŒ… ํ•จ์ˆ˜์—์„œ session, g๋ฅผ import
    • session
      • Flask ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ๊ด€๋ฆฌ, ์‚ฌ์šฉ์ž๋ณ„๋กœ ๊ณ ์œ ํ•œ ๋ฐ์ดํ„ฐ ์ €์žฅํ•˜๋Š” ๊ฐ์ฒด
      • Flask๊ฐ€ ์„ธ์…˜ ๋ฐ์ดํ„ฐ๋ฅผ ์•”ํ˜ธํ™”ํ•ด ๋ณด์•ˆ ์œ ์ง€
      • ์„ธ์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ์‚ฌ์šฉ์ž์˜ ๋กœ๊ทธ์ธ ์ƒํƒœ, ์žฅ๋ฐ”๊ตฌ๋‹ˆ ๋‚ด์—ญ ๋“ฑ์˜ ์ •๋ณด๋ฅผ ์œ ์ง€ํ•˜๊ณ  ์ƒํƒœ๋ฅผ ์ถ”์  ๊ฐ€๋Šฅ
    • g
      • Flask ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋‚ด์—์„œ ๊ธ€๋กœ๋ฒŒํ•˜๊ฒŒ ์‚ฌ์šฉ๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ์ฒด
      • ๋ชจ๋“  ์š”์ฒญ์—์„œ ๊ณต์œ ๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ , ๋‹ค๋ฅธ ํ•จ์ˆ˜๋‚˜ ๋ทฐ์—์„œ ์ด ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผ ๊ฐ€๋Šฅ
      • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ, ํ˜„์žฌ ์‚ฌ์šฉ์ž ์ •๋ณด, ๋กœ๊ทธ ๊ธฐ๋ก ๋“ฑ๊ณผ ๊ฐ™์ด ์—ฌ๋Ÿฌ ๊ณณ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  ๋•Œ ์œ ์šฉ

7. ๐Ÿš€ ๋ฐฐํฌ

AWS Lightsail

  • AWS Lightsail ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•ด ๋ฐฐํฌ
  • ์ €๋ ดํ•˜๊ณ , ์•„์ฃผ ๊ฐ„๋‹จํ•˜๊ฒŒ ๋ฐฐํฌ๊ฐ€ ๊ฐ€๋Šฅํ•ด ์„ ํƒํ•˜์˜€์Œ

์ธ์Šคํ„ด์Šค ์ŠคํŽ™

  • RAM: 1GB
  • vCPU: 2๊ฐœ
  • SSD: 40GB
  • OS: Ubuntu 20.04

์›๊ฒฉ ๊ด€๋ฆฌ ๋ฐฉ๋ฒ• - SSH

  • 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 ํŒŒ์ผ ๋˜ํ•œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ถ„๋ฆฌ
๐Ÿ“ config
โ”œโ”€โ”€โ”€โ”€ ๐Ÿ“„ __init__.py
โ”œโ”€โ”€โ”€โ”€ ๐Ÿ“„ default.py
โ”œโ”€โ”€โ”€โ”€ ๐Ÿ“„ develoment.py
โ””โ”€โ”€โ”€โ”€ ๐Ÿ“„ production.py

์›น ์„œ๋ฒ„์™€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„ ๋ถ„๋ฆฌ

  • ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋Š” ๊ทธ๋ƒฅ 'flask run' ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ตฌ๋™ํ•จ
    • ์ด๋Š” ์ฃผ๋กœ ๊ฐœ๋ฐœ์ด๋‚˜ ๋””๋ฒ„๊น… ๋ชฉ์ ์œผ๋กœ ์‚ฌ์šฉ๋จ
    • ๋‹จ์ผ ์Šค๋ ˆ๋“œ๋กœ ๋™์ž‘ํ•˜๋ฉฐ ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ์™€ ๋†’์€ ํŠธ๋ž˜ํ”ฝ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ์—๋Š” ์ ํ•ฉํ•˜์ง€ ์•Š์Œ
    • ๋ณด์•ˆ๊ณผ ์„ฑ๋Šฅ ์ธก๋ฉด์—์„œ๋Š” ์ด ๋ฐฉ์‹์œผ๋กœ ์ง์ ‘ ์ธํ„ฐ๋„ท์œผ๋กœ ๋…ธ์ถœํ•˜๋Š” ๊ฒƒ์€ ๊ถŒ์žฅ๋˜์ง€ ์•Š์Œ
    • ์‹คํ–‰๊ณผ ์„ค์ •์€ ๋งค์šฐ ํŽธ๋ฆฌํ•จ
  • ์šด์˜ ์„œ๋ฒ„์—์„œ๋Š” WSGI์™€ Web Server๋ฅผ ์‚ฌ์šฉ
    • ์›น ์„œ๋ฒ„์™€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„(WSGI)๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ ๋†’์€ ์„ฑ๋Šฅ, ํ™•์žฅ์„ฑ, ๋ณด์•ˆ ์ œ๊ณต
    • WSGI๋Š” ๋™์  ์ปจํ…์ธ  ์ƒ์„ฑ ๋ฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ์ง ์‹คํ–‰ ๋‹ด๋‹น
    • Web Server๋Š” ์ •์  ํŒŒ์ผ ์„œ๋น™, ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ, ๋ณด์•ˆ ์„ค์ • ๋“ฑ์„ ์ฒ˜๋ฆฌ

WSGI (Web Server Gateway Interface, Whiskey)

  • Python ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ์›น ์„œ๋ฒ„ ๊ฐ„์˜ ํ‘œ์ค€ํ™”๋œ ์ธํ„ฐํŽ˜์ด์Šค
  • WSGI๋Š” ์›น ์„œ๋ฒ„์™€ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ”„๋ ˆ์ž„์›Œํฌ ๋˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋ถ„๋ฆฌํ•ด ๊ฐœ๋ฐœ์ž๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅธ ์„œ๋ฒ„ ๋ฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์กฐํ•ฉํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ค‘๊ฐ„ ๊ณ„์ธต ์—ญํ• ์„ ํ•จ
  • ์ฆ‰, Python์œผ๋กœ ์ž‘์„ฑ๋œ ์—ฌ๋Ÿฌ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ”„๋ ˆ์ž„์›Œํฌ(e.g., Flask, Django ๋“ฑ)์™€ ์›น ์„œ๋ฒ„๋ฅผ ํ†ตํ•ฉํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋จ
  • ๊ตฌํ˜„์ฒด๋กœ๋Š” uwsgi, Gunigcorn ๋“ฑ์ด ๋“ฑ์žฅํ•˜๋ฉด์„œ ํ›จ์”ฌ ํšจ์œจ์ ์ธ ๋™์  ๋ฆฌ์†Œ์Šค ์„œ๋น™์ด ๊ฐ€๋Šฅํ•ด์ง
  • Flask์— WSGI๋ฅผ ๋ถ™์—ฌ WAS๋ฅผ ์šด์šฉํ•˜๊ฒŒ ๋จ
    • Flask๋Š” WSGI ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด๊ณ , WSGI ์„œ๋ฒ„์—์„œ ์š”์ฒญ์„ ๋ฐ›์•„ ๋™์ž‘
Gunicorn
  • WSGI์˜ ๊ตฌํ˜„์ฒด
  • ์šด์˜์„ ์œ„ํ•ด ์šด์˜ ์„œ๋ฒ„์— WSGI์ธ Gunicorn ์„ค์น˜
  • Gunicorn์„ ๋ฆฌ๋ˆ…์Šค์—์„œ ์„œ๋น„์Šค๋กœ ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•ด ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ํŒŒ์ผ ์ƒ์„ฑ ๋ฐ ์„œ๋น„์Šค ํŒŒ์ผ ์ƒ์„ฑ
  • ์„œ๋น„์Šค ์ž๋™ ์‹คํ–‰ ์„ค์ •
sudo systemctl enable ssgrecipe.service

Web Server

  • ์›น ์„œ๋ฒ„๋Š” ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ HTTP ์š”์ฒญ์„ ์ˆ˜์‹ ํ•˜๊ณ , ์ •์  ํŒŒ์ผ(HTML, CSS, ์ด๋ฏธ์ง€ ๋“ฑ)์„ ์„œ๋น„์Šคํ•˜๋ฉฐ, ๋™์  ์ปจํ…์ธ ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„๋‚˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„์— ์š”์ฒญ์„ ์ „๋‹ฌํ•˜๋Š” ์—ญํ• 
  • ์ฃผ๋กœ ์ •์  ์ปจํ…์ธ ๋ฅผ ์„œ๋น„์Šคํ•˜๋Š” ์—ญํ• ์„ ํ•˜๋ฉฐ, ์›น ์„œ๋ฒ„๋กœ๋Š” Apache, Nginx, IIS ๋“ฑ์ด ์žˆ์Œ
  • WSGI ์„œ๋ฒ„๋งŒ ๊ตฌ๋™ํ•ด๋„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ž‘๋™ํ•˜๋Š” ๋ฐ์—๋Š” ๋ฌธ์ œ๊ฐ€ ์—†์Œ, ๊ทผ๋ฐ ์™œ WAS ์•ž์— ์›น ์„œ๋ฒ„๋ฅผ ๋‘๋Š”์ง€?
    • ๋กœ๋“œ ๋ฐธ๋Ÿฐ์‹ฑ: ์—ฌ๋Ÿฌ ์›น ์„œ๋ฒ„ ์ธ์Šคํ„ด์Šค ๊ฐ„์— ํŠธ๋ž˜ํ”ฝ์„ ๋ถ„์‚ฐ
    • ๋ณด์•ˆ ๋ฐ ์ธ์ฆ: SSL/TLS ์ธ์ฆ์„œ ๊ด€๋ฆฌ ๋ฐ ๋ณด์•ˆ ์„ค์ •์„ ์ฒ˜๋ฆฌ
    • ๋ฆฌ๋ฒ„์Šค ํ”„๋ก์‹œ: ๋™์  ์ปจํ…์ธ ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„œ๋ฒ„๋กœ ์š”์ฒญ์„ ์ „๋‹ฌ
    • ์‚ฌ์‹ค ์œ„์™€ ๊ฐ™์€ ๊ธฐ๋Šฅ๋„ ์ด์ œ WSGI, WAS์—์„œ ์ง€์›ํ•˜์ง€๋งŒ, ๊ฒฐ์ •์ ์œผ๋กœ WAS๋Š” ์ž˜ ์ฃฝ์Œ
      • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ์ง์ด ๋™์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ
    • ๋”ฐ๋ผ์„œ ์›น ์„œ๋ฒ„, WAS๋ฅผ ๋‘˜ ๋‹ค ๋‘๊ณ  ์ •์  ๋ฆฌ์†Œ์Šค๊ฐ€ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋ฉด ์›น ์„œ๋ฒ„๋ฅผ ์ฆ์„คํ•˜๊ณ , ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ฆฌ์†Œ์Šค๊ฐ€ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋ฉด WAS ์ฆ์„ค
Nginx
  • ์šด์˜ ์„œ๋ฒ„์˜ ์›น ์„œ๋ฒ„๋กœ Nginx๋ฅผ ์„ค์น˜
  • ๋น„๋™๊ธฐ ๊ธฐ๋ฐ˜ ๊ตฌ์กฐ๋ผ ๋” ์ ์€ ๋ฆฌ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Œ
  • Nginx์˜ ์„ค์ •์„ ๋ณ€๊ฒฝ
    • 80 ํฌํŠธ๋กœ ์„œ๋น„์Šคํ•˜๋„๋ก ํ•จ
    • static ๊ฒฝ๋กœ๋ฅผ ์ง€์ •ํ•ด ์ •์  ๋ฆฌ์†Œ์Šค๋ฅผ ์„œ๋น™ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•จ

Logging

  • ์šด์˜ ํ™˜๊ฒฝ์—์„œ๋Š” ๋ณด์•ˆ ์ด์Šˆ๋กœ FLASK_DEBUG๊ฐ€ False๋กœ ๋˜์–ด์žˆ์œผ๋ฏ€๋กœ ๋กœ๊ทธ๋ฅผ ํŒŒ์ผ๋กœ ์ถœ๋ ฅํ•˜๋„๋ก ํ•จ
  • ์ถœ๋ ฅ๋˜๋Š” ๋กœ๊ทธ ๋ ˆ๋ฒจ์€ 2๋‹จ๊ณ„ INFO, ์ผ๋ฐ˜์ ์ธ ์ •๋ณด๋ฅผ ์ถœ๋ ฅ
    • ์šด์˜ ์„œ๋ฒ„์—์„œ๋Š” ์ฃผ๋กœ INFO ๋ ˆ๋ฒจ, ๊ฐœ๋ฐœ ์„œ๋ฒ„์—์„œ๋Š” DEBUG ๋ ˆ๋ฒจ๋กœ ์„ค์ •ํ•จ
      • ์„ฑ๋Šฅ์ ์ธ ๋ถ€๋ถ„์— ์˜ํ–ฅ์„ ๋ผ์น˜๊ธฐ ๋•Œ๋ฌธ

Database Migration

  • ์šด์˜ ์„œ๋ฒ„์—์„œ๋Š” 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

8. ๐Ÿ”ง Issue & Troubleshooting

โœ…: ํ•ด๊ฒฐ ์ด์Šˆ โ“: ๋ฏธํ•ด๊ฒฐ ์ด์Šˆ

8.1. Open API ๋ฐ์ดํ„ฐ ํ˜ธ์ถœ ์‹คํŒจ

  • ์‹ํ’ˆ ๊ฐ€๊ฒฉ ์ •๋ณด๋ฅผ ๋ฐ›์•„์˜ค๋Š” Open API์—์„œ ์›๋ž˜๋Š” ์ •์ƒ์ ์œผ๋กœ ๋ฐ์ดํ„ฐ ํ˜ธ์ถœ์ด ๋˜์—ˆ์Œ
  • ์–ด๋Š ์ˆœ๊ฐ„๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ ํ˜ธ์ถœ์ด ์‹คํŒจํ•˜๋”๋‹ˆ, ์•„์˜ˆ ๋™์ž‘ํ•˜์ง€ ์•Š๊ธฐ ์‹œ์ž‘
  • ์›์ธ์€ Flask ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰์‹œ ํ˜ธ์ถœ๋˜๋Š” API ํ˜ธ์ถœ ํด๋ผ์ด์–ธํŠธ์— ์ด์ƒํ•œ ๋ฃจํ”„๋ฅผ ์งœ๋†”์„œ ์ผ์ผ ํ˜ธ์ถœ ํ•œ๋„ ์ดˆ๊ณผ๊ฐ€ ๋œ ๊ฒƒ

    โœ… ํ•ด๊ฒฐ: API ํ˜ธ์ถœ ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ˆ˜์ •ํ•˜๊ณ , ์‚ฌ์‹ค์€ API ํ˜ธ์ถœ์€ ํ•œ ๋ฒˆ๋งŒ ํ•ด๋„ ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ณ„๋„์˜ ๋ชจ๋“ˆ๋กœ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉํ–ฅ์œผ๋กœ ๋ณ€๊ฒฝ

8.2. Select Box์—์„œ ๋ฐ์ดํ„ฐ ๋ฐ›์•„์˜ค๊ธฐ ์‹คํŒจ

  • ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ ˆ์‹œํ”ผ๋ฅผ ์ž…๋ ฅ๋ฐ›์„ ๋•Œ, ํ•˜๋‹จ์—์„œ Select box๋กœ ์žฌ๋ฃŒ๋ฅผ ์ž…๋ ฅ ๋ฐ›์Œ
  • ํ•˜์ง€๋งŒ ์„ ํƒ๋œ ๊ฐ’์ด ๊ณ„์† None์œผ๋กœ ๋„˜์–ด์˜ด, ๋ฐ˜๋ฉด ์ˆ˜๋Ÿ‰์„ ์ž…๋ ฅํ•˜๋Š” Text input์€ ์ •์ƒ์ ์œผ๋กœ ๋„˜์–ด์˜ด

    โœ… ํ•ด๊ฒฐ: Form์„ ์ •์˜ํ•  ๋•Œ ์ˆ˜๋Ÿ‰์— ํ•ด๋‹นํ•˜๋Š” ํ•„๋“œ๋Š” Text input์ด๊ธฐ ๋•Œ๋ฌธ์— StringField ๊ฐ์ฒด๊ฐ€ ๋˜์–ด์•ผ ๋˜๋Š” ๊ฒƒ์€ ๋งž์•˜์ง€๋งŒ, ์žฌ๋ฃŒ๋Š” Select box์ด๊ธฐ ๋•Œ๋ฌธ์— Form์—์„œ ํ•„๋“œ๊ฐ€ SelectField์—ฌ์•ผ ๋์Œ

8.3. Select Box ๋™์  ์ƒ์„ฑ

  • ๋ ˆ์‹œํ”ผ๋งˆ๋‹ค ๋“ค์–ด๊ฐ€๋Š” ์žฌ๋ฃŒ์˜ ์ˆ˜๊ฐ€ ๋‹ค์–‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์— Select box์˜ ์ˆ˜๊ฐ€ ๋™์ ์œผ๋กœ ์ถ”๊ฐ€๋์œผ๋ฉด ํ•จ

    โœ… ํ•ด๊ฒฐ: ํ…œํ”Œ๋ฆฟ ์—”์ง„ ํ•˜๋‹จ์— JS ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•จ, ์ถ”๊ฐ€ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ createElement ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ด <div> ํƒœ๊ทธ ์ƒ์„ฑ

8.4. ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•œ Select Box์—์„œ ๋ฐ์ดํ„ฐ ๋ฐ›์•„์˜ค๊ธฐ ์‹คํŒจ

  • 8.2.์—์„œ ํ•ด๊ฒฐํ•œ ๋™์  ์ƒ์„ฑํ•œ Select Box์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๋Š”๋ฐ ์‹คํŒจ
  • ๋ญ”๊ฐ€ ๋™์ ์œผ๋กœ ๋ฐฐ์—ด๋กœ ๋ฐ›๊ณ  ์‹ถ์€๋ฐ, ๋„๋ฌด์ง€ ๋ฐฉ๋ฒ•์„ ๋ชจ๋ฅด๊ฒ ์Œ

    โ“ ๋ฏธํ•ด๊ฒฐ: ๊ฒฐ๊ตญ ํ˜„์žฌ๊นŒ์ง„ Select box ๊ฐœ์ˆ˜๋ฅผ ์ •์ ์œผ๋กœ ์„ค์ •ํ•˜๊ณ  ํ•œ์ •๋œ ์ˆ˜์˜ ์žฌ๋ฃŒ๋งŒ ์ž…๋ ฅ๋ฐ›์„ ์ˆ˜ ์žˆ์Œ, ํ…œํ”Œ๋ฆฟ ์—”์ง„ ๋ฌธ๋ฒ•์ด๋“ , HTML ์ฝ”๋“œ๋“  ๋” ๋”ฅ๋‹ค์ด๋ธŒ ํ•ด๋ณผ ๊ฒƒ

8.5. ์ด๋ฏธ์ง€ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ, ์—‘์Šค๋ฐ•์Šค์™€์˜ ์”จ๋ฆ„

  • ๋ ˆ์‹œํ”ผ์— ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๋ฉด ๊ตฌ์„ฑํ•ด๋‘” ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์ •์ƒ์ ์œผ๋กœ ์‚ฝ์ž…์€ ๋˜์ง€๋งŒ ๋ ˆ์‹œํ”ผ๋ฅผ ์กฐํšŒํ•  ๋•Œ๋Š” HTML์—์„œ ํ•ด๋‹น ๊ฒฝ๋กœ์˜ ์ด๋ฏธ์ง€๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์—†์Œ, ํŽ˜์ด์ง€ ์†Œ์Šค๋ฅผ ๋ณด๋ฉด ๊ฒฝ๋กœ๋Š” ๋ถ„๋ช… ๋งž๋Š”๋ฐ ์—‘์Šค๋ฐ•์Šค๊ฐ€ ๋œธ
  • DB์— ์ •์ƒ์ ์œผ๋กœ ์‚ฝ์ž…์€ ๋˜์ง€๋งŒ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์—†์Œ

    โœ… ํ•ด๊ฒฐ: ์ด๋ฏธ์ง€ ์ €์žฅ ๊ฒฝ๋กœ๋ฅผ ์ž„์˜๋กœ ์ฃผ๋ฉด ์•ˆ ๋˜๊ณ , static ๋””๋ ‰ํ„ฐ๋ฆฌ ์•ˆ์— ์ƒ์„ฑํ•ด์•ผ ์ด๋ฏธ์ง€๋ฅผ ์ •์ƒ์ ์œผ๋กœ ์„œ๋น™ํ•  ์ˆ˜ ์žˆ์Œ

8.6. ํด๋ผ์ด์–ธํŠธ ์ „์šฉ DB, SQLite

  • ์ฒ˜์Œ ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ•  ๋•Œ๋Š” ์šด์˜ ์„œ๋ฒ„๋กœ ๋ฐฐํฌ๊นŒ์ง€๋Š” ํ•  ์ƒ๊ฐ์ด ์—†์–ด์„œ ๋กœ์ปฌ์—์„œ ๊ฐ€๋ณ๊ฒŒ ์“ธ ์ˆ˜ ์žˆ๋Š” ํด๋ผ์ด์–ธํŠธ ์ „์šฉ DB์ธ SQLite๋ฅผ ์‚ฌ์šฉ
  • AWS์— ๋ฐฐํฌํ•˜๊ฒŒ ๋˜๋ฉด์„œ 7.์— ์ž‘์„ฑํ•œ ์ด์œ ๋กœ DB ์„œ๋ฒ„๋ฅผ ๋ถ„๋ฆฌํ•˜๊ณ  ์‹ถ์–ด์ง

    โœ… ํ•ด๊ฒฐ: AWS RDS์— PostgreSQL๋กœ DB ์„œ๋ฒ„๋ฅผ ์ƒ์„ฑ, ORM์„ ์ฑ„ํƒํ•œ ๋•์— ์•„์ฃผ ์‰ฌ์šด ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ž‘์—…์ด ๋˜์—ˆ์Œ

8.7. ๋„๋ฉ”์ธ์ด ์—†์–ด์„œ SSL ์ ์šฉ ๋ถˆ๊ฐ€๋Šฅ

  • SSL ์ธ์ฆ์„œ๋ฅผ ๋ฐœ๊ธ‰ ๋ฐ›์•„์„œ ๋ณด์•ˆ์„ ๊ฐ•ํ™”ํ•˜๊ณ  HTTPS ์ ‘์†์ด ๋˜์—ˆ์œผ๋ฉด ํ•จ

    โ“ ๋ฏธํ•ด๊ฒฐ: ๋„๋ฉ”์ธ ๋„ค์ž„์„ ๋ฐœ๊ธ‰๋ฐ›์ง€ ์•Š์œผ๋ฉด Let's Encrypt์—์„œ SSL ์ธ์ฆ์„œ ๋ฐœ๊ธ‰ ๋ถˆ๊ฐ€๋Šฅ

8.8. ๋˜‘๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ๋ฐฐํฌํ–ˆ๋Š”๋ฐ, ์ž‘๋™์ด ์•ˆ๋˜๋„ค

  • requirements.txt๋ฅผ ํ†ตํ•ด ๊ฐœ๋ฐœ ํ™˜๊ฒฝ๊ณผ ๋˜‘๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์„ค์น˜ํ•˜๊ณ , ์›น ์„œ๋ฒ„์™€ WSGI๋„ ์ •์ƒ์ ์œผ๋กœ ๋Œ์•„๊ฐ€๊ณ  ์žˆ๋Š”๋ฐ ์ž‘๋™์ด ๋˜์ง€ ์•Š์Œ

    โœ… ํ•ด๊ฒฐ: ์šด์˜ ํ™˜๊ฒฝ์—์„œ FLASK_DEBUG๋ฅผ True๋กœ ๋ฐ”๊พธ๊ธฐ ์‹ซ์–ด ๋กœ๊ทธ๋ฅผ txt๋กœ ์ €์žฅํ•˜๋„๋ก ํ•จ, ์ด๋ฅผ ํ†ตํ•ด APP_CONFIG_FILE ์ธ์‹์ด ์•ˆ๋ผ ์˜ค๋ฅ˜๊ฐ€ ๋‚˜๊ณ  ์žˆ์—ˆ์Œ์„ ์•Œ๊ฒŒ๋จ, ์šด์˜ ์„œ๋ฒ„์˜ alias ์„ค์ •์„ ํ†ตํ•ด ํ•ด๊ฒฐ

8.9. ์žฌ๋ฃŒ DB๋ฅผ ์ˆ˜์ง‘ํ•˜๋Š” ๋ฐฉ์•ˆ์— ๋Œ€ํ•œ ์˜๊ฒฌ ์ฐจ์ด

  • ์ด์ƒ์  ๋ชฉํ‘œ๋Š” ์‹ ์„ธ๊ณ„ ๋ฆฌํ…Œ์ผ ๋ธŒ๋žœ๋“œ ๋‚ด ๊ฐ€๊ฒฉ API ์ ์šฉ
  • ์• ์ดˆ์— ํ”„๋กœ์ ํŠธ์˜ ๊ธฐํš ์˜๋„๋Š” ๋ฆฌํ…Œ์ผ ์„œ๋น„์Šค(์‹ ์„ธ๊ณ„ ๋“ฑ) ๋‚ด ์ปค๋ฎค๋‹ˆํ‹ฐ ์ปค๋จธ์Šค ๋ฐœ์ „์ด์—ˆ๊ณ , API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ฐ€๊ฒฉ DB ์—…๋ฐ์ดํŠธ ๊ฐ€๋Šฅ
  • ํ•˜์ง€๋งŒ ์‹ ์„ธ๊ณ„ Open API๊ฐ€ ์‹ ์„ธ๊ณ„ ํ”Œ๋žซํผ์— ์ž…์ ํ•œ ์ƒํ’ˆ ํŒ๋งค์ž์—๊ฒŒ๋งŒ ํ—ˆ์šฉ๋˜์–ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Œ
  • ์ด์— ๋Œ€ํ•œ ๋Œ€์•ˆ์€
    • ๊ณต๊ณต๋ฐ์ดํ„ฐ API
      • ์žฅ์  : ๊ธฐ์ˆ ์ ์œผ๋กœ API ๋„์ž… ๊ธฐ๋Šฅ ๊ตฌํ˜„ ๊ฐ€๋Šฅ
      • ๋‹จ์  : '๋ ˆ์‹œํ”ผ'๋ผ๋Š” ๊ธฐํš ์˜๋„๋ฅผ ์ถฉ์กฑ์‹œํ‚ค๊ธฐ์—๋Š” ๋ฐ์ดํ„ฐ๋กœ ์ œ๊ณต๋˜๋Š” ํ’ˆ๋ชฉ์ด ์ œํ•œ๋จ
    • ์ด๋งˆํŠธ๋ชฐ(์‹ ์„ธ๊ณ„ ๋ฆฌํ…Œ์ผ ๋ธŒ๋žœ๋“œ)์—์„œ ์ง์ ‘ ๊ฐ€๊ฒฉ ์กฐ์‚ฌ
      • ์žฅ์  : ๊ธฐํš ์ทจ์ง€์— ๋งž๊ฒŒ '๋ ˆ์‹œํ”ผ'์— ์ž์ฃผ ๋“ฑ์žฅํ•˜๋Š” ์žฌ๋ฃŒ ์„ ์ • ๊ฐ€๋Šฅ
      • ๋‹จ์  : ๋ฐ์ดํ„ฐ์˜ ์‹ค์‹œ๊ฐ„์„ฑ, ์‹ ๋ขฐ์„ฑ ์ด์Šˆ
  • ํ•ด๋‹น ๋Œ€์•ˆ์„ ์–ด๋–ป๊ฒŒ ์„ ํƒํ•  ์ง€์— ๋Œ€ํ•ด์„œ ํŒ€์› ๊ฐ„์˜ ์˜๊ฒฌ ์ฐจ์ด๊ฐ€ ์žˆ์—ˆ์Œ

    โœ… ํ•ด๊ฒฐ: ๊ณต๊ณต API๋ฅผ ๋„์ž…ํ•˜๊ณ , ์ž„์˜๋กœ ์กฐ์‚ฌํ•œ ๊ฐ€๊ฒฉ ๋ฐ์ดํ„ฐ๋ฅผ ํ˜ผํ•ฉํ•˜์—ฌ DB ๊ตฌ์„ฑ ๊ฐ ๋Œ€์•ˆ์˜ ์žฅ๋‹จ์ ์„ ์ƒํ˜ธ ๋ณด์™„

9. โ˜•๏ธ ํšŒ๊ณ 

๊น€์ง€ํ›ˆ

  • ์ด๋ฒˆ ํ”„๋กœ์ ํŠธ์—์„œ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๋Š” ๊ฒƒ์— ์ฃผ์•ˆ์ ์„ ๋’€์Šต๋‹ˆ๋‹ค. ํŒ€์› ์ „์ฒด๊ฐ€ 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 ์‚ฌ์ง„ ์ €์žฅ ๊ธฐ๋Šฅ์€ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์—ˆ์œผ๋‚˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๋Š” ๊ณผ์ •์—์„œ ์–ด๋ ค์›€์„ ๊ฒช์—ˆ์Šต๋‹ˆ๋‹ค. ์ฐฝ์˜์ ์ธ ์•„์ด๋””์–ด๋กœ ํŒ€์›๋“ค์—๊ฒŒ ์ธ์ •์„ ๋ฐ›์•˜์ง€๋งŒ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜์ง€ ๋ชปํ•˜๋Š” ํ•œ๊ณ„๊ฐ€ ์ €์—๊ฒŒ ๋™๊ธฐ๋ถ€์—ฌ๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ง€์†์ ์ธ ๋ฆฌ๋ทฐ์™€ ์ž๊ธฐ๊ณ„๋ฐœ ๋…ธ๋ ฅ์œผ๋กœ ๋Šฅ๋ ฅ์„ ํ–ฅ์ƒ์‹œํ‚ค๋ฉฐ ๋‹ค์–‘ํ•œ ํ”„๋กœ์ ํŠธ์— ๋„์ „ํ•˜์—ฌ ์ƒ์ƒ ์†์—์„œ๋งŒ ๊ทธ๋ ธ๋˜ ๊ฒƒ๋“ค์„ ํ˜„์‹ค๋กœ ๋งŒ๋“ค์–ด๋‚ด๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published