Skip to content

DavidKimDY/Book-Flask-web-develop

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

11 Commits
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Book-Flask-web-develop

Flask

1. Feature

  • Flask๋Š” ๋งˆ์ดํฌ๋กœ ํ”„๋ ˆ์ž„์›Œํฌ๋‹ค.
  • WSGI ์„œ๋ธŒ์‹œ์Šคํ…œ์€ Werkzeung์—, ํ…œํ”Œ๋ฆฟ์€ jinja2์— ์˜์กดํ•œ๋‹ค.

2. Application Structure

Application Instance

  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•œ๋‹ค.
  • ์›น ์„œ๋ฒ„๋Š” ํด๋ผ์ด์–ธํŠธ๋กœ๋ถ€ํ„ฐ request๋ฅผ ์ˆ˜์‹ ํ•˜๋Š”๋ฐ ์ด๋ฅผ Flask์˜ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ธ์Šคํ„ด์Šค์—์„œ ์ฒ˜๋ฆฌํ•˜๋Š”๋ฐ ์ด๋•Œ WSGI ๋ผ๋Š” protocol์„ ์‚ฌ์šฉํ•œ๋‹ค.

Route and View Function

  • Route : URL๊ณผ URL๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•จ์ˆ˜์˜ ๊ด€๋ จ์„ฑ์„ Route๋ผ๊ณ  ํ•œ๋‹ค. URL์„ ํ•จ์ˆ˜์— ๋งคํ•‘ํ•˜๋Š” ๊ธฐ๋Šฅ
  • View Function : URL์„ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ View Function์ด๋ผ๊ณ  ํ•œ๋‹ค.
  • Flask์—์„œ๋Š” @app.route() decorator๋กœ ๋ผ์šฐํŠธ๋ฅผ ์ •์˜ํ•ด ์ค„ ์ˆ˜ ์žˆ๋‹ค.
  • ๋™์  ์ปดํฌ๋„ŒํŠธ๋Š” @add.route('/user/') ์™€ ๊ฐ™์ด ์ง€์ •ํ•ด ์ค„ ์ˆ˜ ์žˆ๋‹ค
  • ๋™์  ์ปดํฌ๋„ŒํŠธ๋Š” ํƒ€์ž…์„ ์ •์˜ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค. @add.route('/user/<int: id>') int, float, path๋ฅผ ์ง€์›ํ•œ๋‹ค.
  • ๋™์  ์ปดํฌ๋„ŒํŠธ์˜ ์ž…๋ ฅ ํƒ€์ž…์ด ๋‹ค๋ฅผ ๊ฒฝ์šฐ Not Found๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.

Server startup

  • application instance๋Š” run ๋ฉ”์†Œ๋“œ๋กœ ์›น ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•œ๋‹ค.
  • ์„œ๋ฒ„๋ฅผ ์‹คํ–‰ํ•˜๊ณ  ๋‚˜๋ฉด ์„œ๋ฒ„๋Š” ๋ฃจํ”„๋ฅผ ์ง„ํ–‰ํ•œ๋‹ค.
  • ํ”Œ๋ผ์Šคํฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ์›น ์„œ๋ฒ„๋Š” ์ œํ’ˆ ์ƒ์‚ฐ์„ ๋ชฉ์ ์œผ๋กœ ํ•˜์ง€ ์•Š๋Š”๋‹ค.

Request context

  • Flask๊ฐ€ client ์—์„œ request๋ฅผ ์ˆ˜์‹ ํ•˜๋ฉด ์ด๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด view function์—์„œ๋Š” ์—ฌ๋Ÿฌ๊ฐœ์˜ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•œ๋‹ค.

  • requests object๋Š” HTTP requests๋ฅผ ์บก์Šํ™” ํ•œ๋‹ค.

  • view funtion์ด request object์— ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ๋Š” ํ™•์‹คํ•œ ๋ฐฉ๋ฒ•์€ ์ธ์ˆ˜๋กœ์„œ ๋ฐ›๋Š” ๊ฒƒ์ธ๋ฐ ์ด๋Š” ์—ฌ๋ถ„์˜ ์ธ์ˆ˜๋ฅผ ๊ฐ–๋„๋ก ์š”๊ตฌํ•œ๋‹ค. request object ๋งŒ์ด view function์ด access ํ•ด์•ผํ•˜๋Š” ์˜ค๋ธŒ์ ํŠธ๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ธ์ˆ˜๋กœ ๋ฐ›๋Š” ๊ฒƒ์—๋Š” ๋ฌด๋ฆฌ๊ฐ€ ์žˆ๋‹ค. view function์ด ๋‹ค์ˆ˜์˜ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›๋Š”๊ฑธ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด Flask๋Š” ์ปจํ…์ŠคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž„์‹œ์ ์œผ๋กœ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ๊ธ€๋กœ๋ฒŒํ•˜๊ฒŒ ์•ก์„ธ์Šคํ•˜๋„๋ก ํ•œ๋‹ค.

    from flask import request
    
    @app.route('/')
    def index():
    	user_agent = request.headers.get('User-Agent')
    	return f'<p>your brower is {user_agent} </p>'
  • python context

  • ๋‹ค์Œ๊ณผ ๊ฐ™์€ context ๋“ค์ด ์žˆ๋‹ค 17.p

    โ†’ current_app : ํ™œ์„ฑํ™”๋œ app์„ ์œ„ํ•œ app instance

    โ†’ g : request ๋งˆ๋‹ค ์ƒ์„ฑ๋˜๋Š” ์ž„์‹œ ์Šคํ† ๋ฆฌ์ง€ ์˜ค๋ธŒ์ ํŠธ

    โ†’ request : HTTP request context๋ฅผ ์บก์Šํ™” ํ•˜๋Š” ์˜ค๋ธŒ์ ํŠธ

    โ†’ session : ์‚ฌ์šฉ์ž ์„ธ์…˜

Request Dispatch

  • ํด๋ผ์ด์–ธํŠธ์—์„œ request๋ฅผ ์ˆ˜์‹ ํ•˜๋ฉด ๊ทธ๊ฒƒ์„ ์„œ๋น„์Šคํ•˜๊ธฐ ์œ„ํ•œ view function์„ URL์„ ํ†ตํ•ด ์ฐพ๋Š”๋‹ค. ์ด๋•Œ URL map์—์„œ ์ด๋ฅผ ์ฐพ๋Š”๋‹ค. ํŒŒ์ด์ฌ ํŒŒ์ผ์ด๋ฆ„์ด practice.py ๋ผ๊ณ  ํ•œ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค.

    $ python
    >>> from practice import app
    >>> app.url_map
    Map([<Rule '/' (OPTIONS, HEAD, GET) -> index>,
     <Rule '/static/<filename>' (OPTIONS, HEAD, GET) -> static>,
     <Rule '/user/<name>' (OPTIONS, HEAD, GET) -> user>])

Request Hooks

  • request๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์ „ํ›„์— ์ฝ”๋“œ๋ฅผ ๋™์ž‘์‹œ์ผœ์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค. DB connection, ์‚ฌ์šฉ์ž ์ธ์ฆ ๊ฐ™์€ ๊ฒฝ์šฐ๋‹ค.

  • Flask๋Š” request hooks๋ฅผ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ํ˜•ํƒœ๋กœ ์‹คํ–‰ํ•˜๋„๋ก ํ•œ๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์€ 4๊ฐœ์˜ ์ข…๋ฅ˜๊ฐ€ ์žˆ๋‹ค

    โ†’ before_first_request

    โ†’ before_request

    โ†’ after_request

    โ†’ tesrdown_request

  • Request Hooks function๊ณผ View function ์‚ฌ์ด์— ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•˜๊ธฐ ์œ„ํ•ด g context ์ „์—ญ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

Response

  • Flask๋Š” 200์„ ๊ธฐ๋ณธ ์ƒํƒœ ์ฝ”๋“œ๋กœ ์‘๋‹ตํ•œ๋‹ค.

  • 200 ์ด์™ธ์˜ ์ƒํƒœ์ฝ”๋“œ๋ฅผ ๋ฆฌํ„ดํ• ๋•Œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด return์— ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

    @app.route('/')
    def index():
    	return '<h1>Bad Requests</h1>', 400
  • Response object๋ฅผ returnํ•ด์ค„์ˆ˜๋„ ์žˆ๋‹ค. ์ด๋•Œ๋Š” ์‘๋‹ต ์„ค์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ์žฅ์ ์ด ์žˆ๋‹ค.

  • ์•„๋ž˜์ฝ”๋“œ๋Š” ์ฟ ํ‚ค๋ฅผ ์„ค์ •ํ•˜๊ณ  Response object๋ฅผ return ํ•˜๋Š” ์˜ˆ์ด๋‹ค.

from flask import make_response

@app.route('/')
def index():
	response = make_response('<h1>This document carries a cookie </h1>')
	response.set_cookie('answer', '42')
	return response

Redirect

  • ์‘๋‹ต์˜ ํ•œ ์ข…๋ฅ˜์ด๋‹ค.

  • page document๋ฅผ ํฌํ•จํ•˜์ง€ ์•Š์œผ๋ฉฐ ์ƒˆ๋กœ์šด page๋ฅผ ๋กœ๋“œํ•˜๋Š” ์ƒˆ๋กœ์šด URL์„ ๋ธŒ๋ผ์šฐ์ €์— ์ „๋‹ฌํ•œ๋‹ค.

  • Flask๋Š” redirect ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•œ๋‹ค

    from flask import redirect
    
    @app.route('/')
    def index():
    	return redirect('http://www.example.com')
  • ์‘๋‹ต์ฝ”๋“œ๋Š” 302 ์ด๋‹ค.

Flask extension

  • Flask-Script

    โ†’ ์ปค๋งจ๋“œ ๋ผ์ธ ํŒŒ์„œ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ํ™•์žฅ

    โ†’ Flask์˜ startup ์„ค์ • ์˜ต์…˜์„ ์ปค๋งจ๋“œ ๋ผ์ธ์—์„œ ์กฐ์ ˆํ•˜๊ฒŒ ํ•ด์ค€๋‹ค.์ฆ‰, app.run() ํ˜ธ์ถœ์˜ ์ธ์ž๋ฅผ ์ปค๋งจ๋“œ ๋ผ์ธ์—์„œ ์กฐ์ ˆ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•ด์ค€๋‹ค.

    โ†’ ๊ทผ๋ฐ ์ตœ๊ทผ์—๋Š” Flask ์ž์ฒด์— built-in CLI ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค๊ณ  ํ•œ๋‹ค.

    Flask-Script - Flask-Script 0.4.0 documentation

3. Template

  • ํ”Œ๋ผ์Šคํฌ ๋ทฐ ํ•จ์ˆ˜๋Š” ์™„์ „ํžˆ ๋…๋ฆฝ๋œ ๋‘ ๊ฐœ์˜ ๊ธฐ๋Šฅ์ด ๋งˆ์น˜ ํ•˜๋‚˜์˜ ๊ธฐ๋Šฅ์ฒ˜๋Ÿผ ๋ณด์ด๋Š”๋ฐ ์ด๋Š” ์œ ์ง€ ๋ณด์ˆ˜์— ์–ด๋ ค์›€์„ ์ค„ ์ˆ˜ ์žˆ๋‹ค.

  • ๋ทฐ ํ•จ์ˆ˜๋Š” ์ •ํ™•ํžˆ request์— ๋Œ€ํ•œ response๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

  • ์ผ๋ฐ˜์ ์œผ๋กœ request๋Š” application์˜ ์ƒํƒœ๋ฅผ ๋ณ€๊ฒฝํ•œ๋‹ค

    ์‚ฌ์šฉ์ž๊ฐ€ ๊ณ„์ •์„ ์ƒ์„ฑํ•œ๋‹ค โ†’ ์‚ฌ์šฉ์ž๊ฐ€ id์™€ pw๋ฅผ ์ž…๋ ฅํ•˜๊ณ  ์ „์†ก๋ฒ„ํŠผ์„ ๋ˆ„๋ฅธ๋‹ค โ†’ ์„œ๋ฒ„์—์„œ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํฌํ•จํ•˜๋Š” ์‚ฌ์šฉ์ž์˜ request๋ฅผ ๋ฐ›๋Š”๋‹ค โ†’ ํ”Œ๋ผ์Šคํฌ๋Š” request์— ๋Œ€ํ•œ ๋™์ž‘์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ทฐ ํ•จ์ˆ˜์— ์ด request๋ฅผ dispatch ํ•œ๋‹ค โ†’ ๋ทฐ ํ•จ์ˆ˜๋Š” ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์— ์—ฐ๊ฒฐํ•˜์—ฌ ์‚ฌ์šฉ์ž๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค. โ†’ ๊ทธ์—๋Œ€ํ•œ ์‘๋‹ต์„ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด๋‚ธ๋‹ค.

    • ์ด ๋‘ ๊ฐ€์ง€์˜ ํ˜•ํƒœ๋ฅผ ๋น„์ง€๋‹ˆ์Šค ๋กœ์ง ๊ณผ ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ๋กœ์ง ์œผ๋กœ ๋ถ€๋ฅธ๋‹ค.
    • ๋‘๊ฐ€์ง€ ๋กœ์ง์ด ์„ž์—ฌ ์žˆ์œผ๋ฉด ์ฝ”๋“œ์˜ ์œ ์ง€ ๋ณด์ˆ˜๋Š” ์–ด๋ ค์›Œ์ง„๋‹ค.
  • ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ๋กœ์ง์„ ํ…œํ”Œ๋ฆฟ ์œผ๋กœ ์ด๋™์‹œํ‚ค๋Š” ๊ฒƒ์€ application ์œ ์ง€ ๋ณด์ˆ˜์— ๋„์›€์„ ์ค€๋‹ค.

  • ํ…œํ”Œ๋ฆฟ์€ ์‘๋‹ต ํ…์ŠคํŠธ๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ๋Š” ํŒŒ์ผ์ด๋‹ค.

  • ํ…œํ”Œ๋ฆฟ์€ request ๋‚ด์šฉ์—์„œ ์ธ์‹ ๊ฐ€๋Šฅํ•œ ๋™์  ํŒŒํŠธ์— ๋Œ€ํ•œ ๋ณ€์ˆ˜๋“ค์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋‹ค.

  • ๋ณ€์ˆ˜๋“ค์„ ์‹ค์ œ ๊ฐ’์œผ๋กœ ๋ฐ”๊พธ๋Š” ํ”„๋กœ์„ธ์Šค์™€ ์ตœ์ข… ์‘๋‹ต ๋ฌธ์ž์—ด์„ ๋ฆฌํ„ดํ•˜๋Š” ํ”„๋กœ์„ธ์Šค๋ฅผ ๋ Œ๋”๋ง ์ด๋ผ๊ณ  ํ•œ๋‹ค

  • ํ…œํ”Œ๋ฆฟ์„ ๋ Œ๋”๋งํ•˜๋Š” ์ž‘์—…์„ ์œ„ํ•ด Flask๋Š” Jinja2 ๋ผ๋Š” ๊ฐ•๋ คํฌํ•œ ํ…œํ”Œ๋ฆฟ ์—”์ง„์„ ์‚ฌ์šฉํ•œ๋‹ค

Jinja2 Template Engine

  • Jinja2 Tamplate๋Š” response text๋ฅผ includeํ•˜๋Š” file์ด๋‹ค.
  • ๋ณดํ†ต ํ”Œ๋ผ์Šคํฌ์—์„œ๋Š” application folder ์•ˆ์— ์œ„์น˜ํ•˜๋Š” tamplates ์„œ๋ธŒํด๋”์—์„œ ํ…œํ”Œ๋ฆฟ์„ ๊ฒ€์ƒ‰ํ•œ๋‹ค.
  • ํ”Œ๋ผ์Šคํฌ์—์„œ Template์„ rendering ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” render_template ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
from flask import Flask, render_template
  • ํ…œํ”Œ๋ฆฟ์—์„œ ์‚ฌ์šฉ๋˜๋Š” {{name}} ์€ ๋ณ€์ˆ˜๋ฅผ ์˜๋งˆํ•œ๋‹ค.

  • Jinja2 ๋Š” ์–ด๋–ค ํƒ€์ž…์˜ ๋ณ€์ˆ˜๋ผ๋„ ์ธ์‹ํ•œ๋‹ค.

  • ๋ณ€์ˆ˜๋Š” filter๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค

    Hello, {{name|capitalize}}
  • Jinja2๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ชจ๋“  ๋ณ€์ˆ˜๋ฅผ ๋ณด์•ˆ ๋ชฉ์ ์œผ๋กœ ์ด์Šค์ผ€์ดํ”„ํ•œ๋‹ค.

  • Jinja2๋Š” If๋ฌธ, for๋ฌธ, macro(like function)๋ฌธ ๋“ฑ์„ ํ…œํ”Œ๋ฆฟ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค. ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ import, include, ์ƒ์†๋“ฑ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.

Flask-Bootstrap๊ณผ Tweeter-Bootstrap์˜ ํ†ตํ•ฉ

  • Bootstrap์€ ํŠธ์œ„ํ„ฐ์—์„œ ์ œ๊ณตํ•˜๋Š” ์˜คํ”ˆ์†Œ์Šค ํ”„๋ ˆ์ž„์›Œํฌ๋‹ค.
  • Bootstrap์€ ๋ชจ๋“  ์›น ๋ธŒ๋ผ์šฐ์ €์™€ ํ˜ธํ™˜๋˜๋Š” ๊น”๋”ํ•˜๊ณ  ๋งค๋ ฅ์ ์ธ ์›น ํŽ˜์ด์ง€๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋กํ•˜๋Š” ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
  • Bootstrap์€ client ์ธก์˜ ํ”„๋ ˆ์ž„์›Œํฌ๋‹ค.
  • ์„œ๋ฒ„์—์„œ๋Š” ๋ถ€ํŠธ์ŠคํŠธ๋žฉ์˜ CSS์™€ JavaScript ํŒŒ์ผ์„ ์ฐธ์กฐํ•˜๋Š” HTML ์‘๋‹ต์„ ์ œ๊ณตํ•ด์•ผ ํ•œ๋‹ค.
  • ๋˜ํ•œ ์„œ๋ฒ„๋Š” HTML, CSS, JavaScript ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด์„œ ์›ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋“ค์„ ์ธ์Šคํ„ด์Šคํ™” ํ•œ๋‹ค.
  • ์ด๋Ÿฐ ์ž‘์—…์„์€ ํ…œํ”Œ๋ฆฟ์—์„œ ์ด๋ฃจ์–ด์ง€๊ธฐ์— ์ ํ•ฉํ•˜๋‹ค.
  • ๋ถ€ํŠธ์ŠคํŠธ๋žฉ๊ณผ APP์„ ํ†ตํ•ฉํ•˜๋Š” ๊ฐ€์žฅ ํ™•์‹คํ•œ ๋ฐฉ๋ฒ•์€ ํ•„์š”ํ•œ ๋ชจ๋“  ๋ณ€๊ฒฝ ์‚ฌํ•ญ๋“ค์„ ํ…œํ”Œ๋ฆฟ์— ๋งŒ๋“ค์–ด ๋‘๋Š” ๊ฒƒ์ด๋‹ค.
  • flask-bootstrap ์„ ์„ค์น˜ํ•˜์ž
  • ํ”Œ๋ผ์Šคํฌ์˜ ํ™•์žฅ์€ ๋ณดํ†ต App instance๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ ์ดˆ๊ธฐํ™”๋œ๋‹ค.

โš ๏ธ import flask.ext ๋Š” ๋”์ด์ƒ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค

  • Flask-Bootstrap์ด ํ•œ๋ฒˆ ์ดˆ๊ธฐํ™”๋˜๋ฉด ๋ชจ๋“  ๋ถ€ํŠธ์ŠคํŠธ๋žฉ ํŒŒ์ผ์„ ํฌํ•จํ•˜๋Š” ๋ฒ ์ด์Šค ํ…œํ”Œ๋ฆฟ์„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Jinja2 extends ๋Š” ํ…œํ”Œ๋ฆฟ ์ƒ์†์„ ๊ตฌํ˜„ํ•œ๋‹ค.
  • Flask-Bootstrap์˜ ๋ฒ ์ด์Šค ํ…œํ”Œ๋ฆฟ์€ ๋ชจ๋“  ๋ถ€ํŠธ์ŠคํŠธ๋žจ CSS, JavaScript๋ฅผ ํฌํ•จํ•˜๋Š” ์Šค์ผˆ๋ ˆํ†ค ์›น ํŽ˜์ด์ง€๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
  • ๋ฒ ์ด์Šค ํ…œํ”Œ๋ฆฟ์€ ํŒŒ์ƒ๋œ ํ…œํ”Œ๋ฆฟ์ด ์˜ค๋ฒ„๋ผ์ด๋“œํ•  ์ˆ˜ ์žˆ๋„๋ก ๋ธ”๋ก์„ ์ •์˜ํ•œ๋‹ค.
  • ๋ธ”๋ก์€ {% block %} and {% endblock %} ์œผ๋กœ ์ •์˜ํ•œ๋‹ค.

๋งํฌ

  • ๋‚ด๋น„๊ฒŒ์ด์…˜ ๋ฐ”์™€ ๊ฐ™์ด ์„œ๋กœ ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋“ค์„ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ์„ ๋งํฌ๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค
  • ๋™์  ๋ผ์šฐํŠธ๋ฅผ ์œ„ํ•ด ํ…œํ”Œ๋ฆฟ์—์„œ URL์„ ๊ตฌ์„ฑํ•˜๋Š” ๊ฒƒ์€ ์˜์กด์„ฑ์ด ๋ฐœ์ƒํ•˜๊ฑฐ๋‚˜ ๋ผ์šฐํŠธ ์žฌ๊ตฌ์„ฑ์œผ๋กœ ๋งํฌ๊ฐ€ ๊นจ์งˆ ์ˆ˜ ์žˆ๋‹ค.
  • Flask๋Š” url_for() ํ—ฌํผ ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
  • ์ด ํ•จ์ˆ˜๋Š” URL ๋งต์— ์ €์žฅ๋œ ์ •๋ณด๋ฅผ ์ด์šฉํ•œ๋‹ค.
  • ์ด ํ•จ์ˆ˜๋Š” ํ•˜๋‚˜์˜ ๋ทฐ ํ•จ์ˆ˜ ์ด๋ฆ„ (ํ˜น์€ app.add_url_route( )์™€ ํ•จ๊ป˜ ์ •์˜๋œ ๋ผ์šฐํŠธ๋ฅผ ์œ„ํ•œ endpoint ์ด๋ฆ„) ๊ฐ–๊ณ  URL์„ ๋ฆฌํ„ดํ•œ๋‹ค.
  • ์‚ฌ์šฉ๋ฒ•์€ href = {{ url_for('user', name= 'David') }}

์ •์  ํŒŒ์ผ

  • Flask๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ application์˜ root ํด๋”์— ์žˆ๋Š” static์ด๋ผ๊ณ  ํ•˜๋Š” ์„œ๋ธŒ๋””๋ ‰ํ† ๋ฆฌ์—์„œ ์ •์  ํŒŒ์ผ์„ ์ฐพ๋Š”๋‹ค.

Flask-Moment๋ฅผ ์ด์šฉํ•œ ๋‚ ์งœ์™€ ์‹œ๊ฐ„ ์ง€์—ญํ™”

  • ์›น ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋‚ ์งœ์™€ ์‹œ๊ฐ„์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ „ ์„ธ๊ณ„์—์„œ ๋‹ค๋ฅธ ์‹œ๊ฐ„๋Œ€๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ„๋‹จํ•œ ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ๋‹ค.
  • ์„œ๋ฒ„๋Š” UTC๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์›น ๋ธŒ๋ผ์šฐ์ €๋Š” ์ „์†ก๋ฐ›์€ ์‹œ๊ฐ„ ์œ ๋‹›์„ ์ž์‹ ์˜ ๋กœ์ปฌ ์‹œ๊ฐ„์œผ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ  ๋ Œ๋”๋งํ•œ๋‹ค.

4. Web Form

  • Flask-WTF ํ™•์žฅ์€ ์›น ํผ์„ ์‚ฌ์šฉํ•˜์—ฌ ํ›จ์”ฌ ๋ฉ‹์ง„ ๊ฒฝํ—˜์„ ํ•˜๋„๋ก ๋„์™€์ค€๋‹ค

ํฌ๋กœ์Šค-์‚ฌ์ดํŠธ ๋ฆฌํ€˜์ŠคํŠธ ์œ„์กฐ(CSRF) ๋ณดํ˜ธ

  • Flask-WTF๋Š” ํฌ๋กœ์Šค ์‚ฌ์ดํŠธ ๋ฆฌํ€˜์ŠคํŠธ ์œ„์กฐ (CSRF) ๊ณต๊ฒฉ์œผ๋กœ๋ถ€ํ„ฐ ๋ชจ๋“  ํผ์„ ๋ณดํ˜ธํ•œ๋‹ค.

  • CSRF ๊ณต๊ฒฉ์€ ์•…์˜์  ์›น์‚ฌ์ดํŠธ์—์„œ ํฌ์ƒ์ž๊ฐ€ ๋กœ๊ทธ์ธํ•œ ๋‹ค๋ฅธ ์›น์‚ฌ์ดํŠธ๋กœ ๋ฆฌํ€˜์ŠคํŠธ๋ฅผ ์ „์†กํ•  ๋•Œ ์ผ์–ด๋‚œ๋‹ค.

  • CSRF ๋ณดํ˜ธ๋ฅผ ์œ„ํ•ด Flask-WTF๋Š” ์•”ํ˜ธํ™” ํ‚ค๋ฅผ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•œ app์ด ํ•„์š”ํ•˜๋‹ค.

  • Flask-WTF๋Š” ์ด ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•”ํ˜ธํ™”๋œ ํ† ํฐ์„ ์ƒ์„ฑํ•˜๊ณ  ์ด ํ† ํฐ์€ ํผ ๋ฐ์ดํ„ฐ์™€ ํ•จ๊ป˜ ๋ฆฌํ€˜์ŠคํŠธ๋ฅผ ์ธ์ฆํ•œ๋‹ค.

  • ์•”ํ˜ธ ์„ค์ • ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค

    app = Flask(__name__)
    app.config['SECRET_KEY'] = 'secret key'
  • app.config ๋Š” ์„ค์ • ๋ฉด์ˆ˜๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ณต๊ฐ„์ด๋‹ค.

  • ๋ณด์•ˆ์„ฑ ํ–ฅ์ƒ์„ ์œ„ํ•ด ๋ณด์•ˆ ํ‚ค๋Š” ํ™˜๊ฒฝ๋ณ€์ˆ˜์— ์ €์žฅํ•œ๋‹ค.

Form class

  • Flask-WTF์€ Form ํด๋ž˜์Šค๋กœ๋ถ€ํ„ฐ ์ƒ์†ํ•œ ํด๋ž˜์Šค์— ์˜ํ•ด ํ‘œํ˜„๋œ๋‹ค.
  • ๊ฐ ํ•„๋“œ์˜ ์˜ค๋ธŒ์ ํŠธ๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ ๊ฒ€์ฆ์ž๊ฐ€ ๋ถ™์–ด ์žˆ๋‹ค. ๊ฒ€์ฆ์ž๋Š” ์‚ฌ์šฉ์ž์˜ ์ž…๋ ฅ๊ฐ’์ด ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธํ•œ๋‹ค.
  • WTFrom์—์„œ๋Š” HTML ํ•„๋“œ๋ฅผ ์™€ ๊ฒ€์ฆ์ž๋ฅผ ์ง€์›ํ•œ๋‹ค.
  • HTML ํ•„๋“œ๋Š” ํ…์ŠคํŠธ ํ•„๋“œ, ํŒจ์Šค์›Œ๋“œ ํ•„๋“œ, True or False๊ฐ’ ํ•„๋“œ ๋“ฑ๋“ฑ์ด ์žˆ๋‹ค.
  • ๊ฒ€์ฆ์ž๋Š” ์ด๋ฉ”์ผ ์ฃผ์†Œ๊ฒ€์ฆ, IPv4 ๊ฒ€์ฆ, ์ž…๋ ฅ๊ฐ’ ๋ฌธ์ž์—ด ๊ธธ์ด ๊ฒ€์ฆ ๋“ฑ๋“ฑ์ด ์žˆ๋‹ค.

Form์˜ HTML Rendering

  • ํผ ํ•„๋“œ๋Š” ํ˜ธ์ถœ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

  • ํผ ํ•„๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด ํ…œํ”Œ๋ฆฟ์€ HTML๋กœ ๋žœ๋”๋ง ๋œ๋‹ค.

  • Flask-Bootstrap์€ ์ „์ฒด Flask-WTF ํผ์„ ๋žœ๋”๋งํ•˜๊ธฐ ์œ„ํ•ด ๋ถ€ํŠธ์ŠคํŠธ๋žฉ์˜ ๋ฏธ๋ฆฌ ์ •์˜๋œ ํผ ์Šคํƒ€์ผ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ—ฌํผ ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•˜๋Š”๋ฐ ํ•œ ๋ฒˆ์˜ ํ˜ธ์ถœ๋กœ ์ด๋ฃจ์–ด์ง€๊ณ  ์ด์ „ ํผ์ด ๋žœ๋”๋ง ๋œ๋‹ค.

    {% import "bootstrap/wtf.html" as wtf %}
    {{ wtf.quick_form(form) }}

    View Function์—์„œ์˜ Form ์ฒ˜๋ฆฌ

    • app.route ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์— ์ถ”๊ฐ€๋œ methods ์ธ์ˆ˜๋Š” ํ”Œ๋ผ์Šคํฌ์—์„œ URL ๋งต์˜ GET๊ณผ POST ๋ฆฌํ€˜์ŠคํŠธ๋ฅผ ์œ„ํ•œ ํ•ธ๋“ค๋Ÿฌ๋กœ ๋ทฐ ํ•จ์ˆ˜๋ฅผ ๋“ฑ๋กํ•˜๋„๋ก ํ•œ๋‹ค.
    • ์„œ๋ธŒ๋ฏธ์…˜์€ ๋˜๋„๋ก์ด๋ฉด POST ๋ฆฌํ€˜์ŠคํŠธ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค. GET์—์„œ๋„ ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๊ฐ€ URL์— ํฌํ•จ๋˜์–ด ๋‚ ์•„๊ฐ€์„œ ๋ณด์•ˆ์ƒ์˜ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.

    ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ์™€ ์‚ฌ์šฉ์ž์„ธ์…˜

    no-rediction

    • POST ๋ฐฉ์‹์œผ๋กœ ์š”์ฒญํ•˜๊ณ ์„œ ์ƒˆ๋กœ๊ณ ์นจ์„ ๋ˆŒ๋ €์„๋•Œ ์ข…์ข… ์ผ์–ด๋‚˜๋Š” ๋ฐ˜์‘์ด๋‹ค. ์ฆ‰, ์ƒˆ๋กœ๊ณ ์นจ์€ ํผ ์„œ๋ธŒ๋ฏธ์…˜์„ ๋‘ ๋ฒˆ ํ•˜๊ฒŒ๋˜๋Š” ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚จ๋‹ค.
    • ๋”ฐ๋ผ์„œ ์›น app์ด ๋ธŒ๋ผ์šฐ์ €์— ์˜ํ•ด ์ „์†ก๋œ ๋งˆ์ง€๋ง‰ ์š”์ฒญ์œผ๋กœ POST ์š”์ฒญ์„ ๋‚จ๊ธฐ์ง€ ์•Š๋Š” ๊ฒƒ์ด ์ข‹์€ ์Šต๊ด€์ด๋‹ค.
    • ์ด ์Šต๊ด€์€ ์ •์ƒ์ ์ธ ์‘๋‹ต ๋Œ€์‹ ์— ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ์™€ ํ•จ๊ป˜ POST ์š”์ฒญ์— ๋Œ€ํ•ด ์‘๋‹ตํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋ฅผ Post/Redirect/Get pattern ์ด๋‹ค.
    • ํ•ฉ๋ฆฌ์ ์ด๊ณ  ์ •๋‹นํ•œ ์ผ๋“ค์— ์ธ๋‚ดํ•˜๊ณ  ๋ถˆํ•ฉ๋ฆฌํ•˜๊ณ  ๋ถˆ์˜ํ•œ ์ผ์— ์ฐธ์ง€ ์•Š๋Š” ๊ฒƒ์ด ๋ฐ”๋ฅด๊ฒŒ ์ธ์ƒ์„ ์‚ฌ๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.
    • ์œ„ ๋ฐฉ๋ฒ•์—๋Š” ๋ฌธ์ œ์ ์ด ์žˆ๋‹ค. ๋ฆฌ๋‹ค์ด๋ ‰์…˜ํ•˜๋Š” ์ˆœ๊ฐ„ Form data๋Š” ์‚ฌ๋ผ์ง„๋‹ค. ๋”ฐ๋ผ์„œ app์€ ์‚ฌ์šฉ์ž์˜ Form data๋ฅผ ์ €์žฅํ•ด๋‘์–ด์•ผ ํ•œ๋‹ค.
    • app์€ ์‚ฌ์šฉ์ž ์„ธ์…˜์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜์—ฌ ์‚ฌ์šฉ์ž์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ์–ตํ•ด๋‚ธ๋‹ค.
    • ๊ธฐ๋ณธ์ ์œผ๋กœ ์‚ฌ์šฉ์ž ์„ธ์…˜์€ ํด๋ผ์ด์–ธํŠธ ์ธก ์ฟ ํ‚ค์— ์ €์žฅ๋œ๋‹ค. ์ด๋Š” ์„ค์ •๋˜์–ด ์žˆ๋Š” SECRET_KEY๋กœ ์•”ํ˜ธํ™”๋˜์–ด ์žˆ๋‹ค. ์ฟ ํ‚ค์˜ ์ฝ˜ํ…์ธ ๊ฐ€ ์กฐ์ž‘๋˜๋ฉด ์„œ๋ช…์ด ๋ฌดํšจํ™”๋˜๊ณ  ๊ฒฐ๊ตญ ์„ธ์…˜ ์ž์ฒด๊ฐ€ ๋ฌดํšจํ™”๋œ๋‹ค.
    • ์„ธ์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ๋กœ์ปฌ ๋ณ€์ˆ˜์˜€๋˜ name์€ session['name'] ์œผ๋กœ ์ €์žฅ๋˜์—ˆ๊ณ  ์ดํ›„ ๋ฆฌํ€˜์ŠคํŠธ์—์„œ๋„ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๋‹ค.

5. DataBase

SQL DataBase

  • Structured Query Language
  • ๊ด€๊ณ„ํ˜• ๋ชจ๋ธ์ด๋ผ๊ณ  ํ•œ๋‹ค.
  • ํ…Œ์ด๋ธ”์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•œ๋‹ค.
  • Column์˜ ์ˆ˜๋Š” ํ…Œ์ด๋ธ”๋งˆ๋‹ค ๊ณ ์ •๋˜์–ด ์žˆ๋‹ค.
  • Row ์ˆ˜๋Š” ๋Š˜์–ด๋‚œ๋‹ค.
  • ๊ธฐ๋ณธํ‚ค(primary key)์™€ ์™ธ๋ž˜ํ‚ค(foreign key)๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๊ธฐ๋ณธํ‚ค๋Š” ํ•ด๋‹น ํ–‰์˜ ์‹๋ณ„์ž์ด๊ณ  ์™ธ๋ž˜ํ‚ค๋Š” ์ด๋ฅผ ์ฐธ์กฐํ•œ๋‹ค.

NoSQL DataBase

  • ํ…Œ์ด๋ธ” ๋Œ€์‹  ์ปฌ๋ž™์…˜(collection)
  • ๋ ˆ์ฝ”๋“œ ๋Œ€์‹  ๋„ํ๋จผํŠธ(document) ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์—ญ์ •๊ทœํ™”(denormalization)๋ผ๊ณ  ํ•˜๋Š” ์˜คํผ๋ ˆ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉฐ ๋ฐ์ดํ„ฐ ์ค‘๋ณต์„ ๋ฐฉ์ง€ํ•œ๋‹ค.

ORM

  • ๊ฐ์ฒด์™€ ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋™์œผ๋กœ ๋งคํ•‘(์—ฐ๊ฒฐ)ํ•ด์ฃผ๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค.
  • ๊ฐ์ฒด ์ง€ํ–ฅ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์€ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ๊ด€๊ณ„ํ˜• ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” ํ…Œ์ด๋ธ”์„ ์‚ฌ์šฉํ•œ๋‹ค.
  • ๊ฐ์ฒด ๋ชจ๋ธ๊ณผ ๊ด€๊ณ„ํ˜• ๋ชจ๋ธ ๊ฐ„์— ๋ถˆ์ผ์น˜๊ฐ€ ์กด์žฌํ•œ๋‹ค.
  • ORM์„ ํ†ตํ•ด ๊ฐ์ฒด ๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ SQL์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜์—ฌ ๋ถˆ์ผ์น˜๋ฅผ ํ•ด๊ฒฐํ•œ๋‹ค.

E-mail

Flask-Mail

  • Flask-Mail์€ SMTP (Simple Mail Tranfer Protocol) ์„œ๋ฒ„์™€ ์—ฐ๊ฒฐํ•˜์—ฌ ์ด๋ฉ”์ผ์„ ์„œ๋ฒ„์— ์ „๋‹ฌํ•จ์œผ๋กœ ์ด๋ฉ”์ผ์„ ๋ฐœ์†กํ•œ๋‹ค.

  • ๋‹ค๋ฅธ ์„ค์ •์ด ์—†๋‹ค๋ฉด Flask-Mail์€ ํฌํŠธ 25๋ฒˆ์„ ํ†ตํ•ด localhost ์™€ ์—ฐ๊ฒฐํ•œ๋‹ค.

  • ์ด๋•Œ๋Š” ์ธ์ฆ์—†์ด ์ด๋ฉ”์ผ์„ ๋ฐœ์†กํ•œ๋‹ค.

  • ์™ธ๋ถ€ SMTP ์„œ๋ฒ„๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ์ด ๋” ํŽธํ•  ์ˆ˜ ์žˆ๋‹ค. -> google์˜ smtp๋ฅผ ์ด์šฉํ•œ๋‹ค

  • ์™ธ๋ถ€ SMTP ์„œ๋ฒ„๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ์ด ๋” ํŽธํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ๋‹ค์Œ์€ ๊ตฌ๊ธ€์˜ Gmail ๊ณ„์ •์„ ํ†ตํ•ด ์ด๋ฉ”์ผ์„ ์ „์†กํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

    app.config['MAIL_SERVER'] = 'smtp.googlemail.com'
    app.config['MAIL_PORT'] = 587
    app.config['MAIL_USE_TLS'] = True
    app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME')
    app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD') 
    • ๊ฐœ์ธ์ •๋ณด๋Š” ์ ˆ๋Œ€๋กœ ์Šคํฌ๋ฆฝํŠธ์— ์ž…๋ ฅํ•˜์ง€ ๋ง๊ณ  ์œ„์™€ ๊ฐ™์ด ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

    • ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค

      export MAIL_USERNAME=๊ตฌ๊ธ€์•„์ด๋””
      export MAIL_PASSWORD=๋น„๋ฐ€๋ฒˆํ˜ธ

      โš ๏ธ ๋งŒ์•ฝ ์‹ค์Šต์— ์‚ฌ์šฉํ•˜๋Š” ๊ตฌ๊ธ€์•„์ด๋””๊ฐ€ 2-step ์ธ์ฆ์ด๋ผ๋ฉด smtplib.SMTPAuthenticationError: (535, b'5.7.8 Username and Password not accepted. Learn more at\n5.7.8 [https://support.google.com/mail/?p=BadCredentials](https://support.google.com/mail/?p=BadCredentials) ์ด๋ผ๋Š” Error๊ฐ€ ๋œฐ ๊ฒƒ์ด๋‹ค. ๊ทธ๋•Œ๋Š” Google Account์— Less secure app access ์—์„œ Allow Less Security App ์„ On ์œผ๋กœ ์„ค์ •ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

Application์—์„œ Email ๊ธฐ๋Šฅ

  • ์ด๋ฉ”์ผ ์ „์†ก ๊ธฐ๋Šฅ์„ ํ•จ์ˆ˜๋กœ ์ถ”์ƒํ™” ํ•˜๋Š” ๊ฒƒ์„ ์ถ”์ฒœํ•œ๋‹ค.
  • ์ด ํ•จ์ˆ˜๋Š” Jinja2 ํ…œํ”Œ๋ฆฟ์œผ๋กœ ์ด๋ฉ”์ผ ๋ณธ๋ฌธ์„ ๋ Œ๋”๋งํ•œ๋‹ค.
    • code์—์„œ๋Š” ์ด๋ฉ”์ผ ํ…œํ”Œ๋ฆฟ์„ mail directory์— ๋ชจ์•„๋‘์—ˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

Code ์„ค๋ช…

Book-Flask-web-develop/email.py at main ยท DavidKimDY/Book-Flask-web-develop

๋น„๋™๊ธฐ ์ด๋ฉ”์ผ ์ „์†ก

  • ์œ„์˜ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ด๋ณด๋ฉด mail์„ ๋ณด๋‚ด๋Š” ๋™์•ˆ web browser๊ฐ€ ๋ฉˆ์ถ”๋Š” ๋ชจ์Šต์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฆฌํ€˜์ŠคํŠธ ํ•ธ๋“ค๋ง์„ ํ•˜๋Š” ๋™์•ˆ ๋ถˆํ•„์š”ํ•œ ์ง€์—ฐ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด, ์ด๋ฉ”์ผ ์ „์†ก ํ•จ์ˆ˜๋Š” ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค.
def send_async_email(app, msg):
    with app.app_context():
        mail.send(msg)

def send_email(to, subject, template, **kwargs):
    msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + ' ' + subject,
                  sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to])
    msg.body = render_template(template + '.txt', **kwargs)
    msg.html = render_template(template + '.html', **kwargs)
    thr = Thread(target=send_async_email, args=[app, msg])
    thr.start()
    return thr
  • ๋งŽ์€ ํ”Œ๋ผ์Šคํฌ ํ™•์žฅ์€ ํ™œ์„ฑํ™”๋œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ๋ฆฌํ€˜์ŠคํŠธ ์ปจํ…์ŠคํŠธ๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฐ€์ •ํ•˜์— ๋™์ž‘ํ•œ๋‹ค.
  • Flask-Mail์€ send() ํ•จ์ˆ˜์—์„œ current_app์„ ์‚ฌ์šฉํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ปจํ…์ŠคํŠธ๊ฐ€ ํ™œ์„ฑํ™”๋  ๊ฒƒ์„ ์š”๊ตฌํ•œ๋‹ค. ํ•˜์ง€๋งŒ send() ํ•จ์ˆ˜๊ฐ€ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ์—์„œ ์‹คํ–‰๋˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ปจํ…์ŠคํŠธ๊ฐ€ ์ธ์œ„์ ์œผ๋กœ app.app_context()๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•œ๋‹ค.
  • ๋Œ€์šฉ๋Ÿ‰ ์ด๋ฉ”์ผ ์ „์†ก ์ž‘์—…๊ณผ ๊ฐ™์€ ๊ฒฝ์šฐ์—๋Š” ์ด๋ฉ”์ผ ์ „์†ก์„ ํ•˜๋‚˜์˜ ์ž‘์—…์œผ๋กœ ๋งŒ๋“œ๋Š” ๊ฒƒ์ด ์ด๋ฉ”์ผ์„ ์ „์†กํ•  ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์Šค๋ ˆ๋“œ๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋” ๋‚ซ๋‹ค.
  • ์˜ˆ๋ฅผ ๋“ค๋ฉด send_async_email() ํ•จ์ˆ˜์˜ ์‹คํ–‰์€ Celery ํƒœ์Šคํฌ ํ์— ์ „์†กํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

๋Œ€๊ทœ๋ชจ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์กฐ

  • ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ปค์งˆ์ˆ˜๋ก ํ•˜๋‚˜์˜ ์Šคํฌ๋ฆฝํŠธ ์†Œ์Šค ํŒŒ์ผ์—์„œ ์ž‘์—…ํ•˜๋Š” ๊ฒƒ์€ ๋น„ํšจ์œจ์ ์ด๋‹ค.
  • ๋””๋ฅธ ํ”„๋ ˆ์ž„์›Œํฌ์™€ ๋‹ฌ๋ฆฌ ํ”Œ๋ผ์Šคํฌ๋Š” ํŠน์ • ๊ตฌ์กฐ๋ฅผ ์š”๊ตฌํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • ๋”ฐ๋ผ์„œ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๊ฒƒ์€ ๊ฐœ๋ฐœ์ž์˜ ๋ชซ์ด๋‹ค.

ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

flasky/
โ”œโ”€โ”€ LICENSE
โ”œโ”€โ”€ README.md
โ”œโ”€โ”€ app
โ”‚ย ย  โ”œโ”€โ”€ __init__.py
โ”‚ย ย  โ”œโ”€โ”€ email.py
โ”‚ย ย  โ”œโ”€โ”€ main
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ __init__.py
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ errors.py
โ”‚ย ย  โ”‚ย ย  โ”œโ”€โ”€ forms.py
โ”‚ย ย  โ”‚ย ย  โ””โ”€โ”€ views.py
โ”‚ย ย  โ”œโ”€โ”€ models.py
โ”‚ย ย  โ”œโ”€โ”€ static
โ”‚ย ย  โ”‚ย ย  โ””โ”€โ”€ favicon.ico
โ”‚ย ย  โ””โ”€โ”€ templates
โ”‚ย ย      โ”œโ”€โ”€ 404.html
โ”‚ย ย      โ”œโ”€โ”€ 500.html
โ”‚ย ย      โ”œโ”€โ”€ base.html
โ”‚ย ย      โ”œโ”€โ”€ index.html
โ”‚ย ย      โ””โ”€โ”€ mail
โ”‚ย ย          โ”œโ”€โ”€ new_user.html
โ”‚ย ย          โ””โ”€โ”€ new_user.txt
โ”œโ”€โ”€ config.py
โ”œโ”€โ”€ flasky.py
โ”œโ”€โ”€ migrations
โ”‚ย ย  โ”œโ”€โ”€ README
โ”‚ย ย  โ”œโ”€โ”€ alembic.ini
โ”‚ย ย  โ”œโ”€โ”€ env.py
โ”‚ย ย  โ”œโ”€โ”€ script.py.mako
โ”‚ย ย  โ””โ”€โ”€ versions
โ”‚ย ย      โ””โ”€โ”€ 38c4e85512a9_initial_migration.py
โ”œโ”€โ”€ requirements.txt
โ””โ”€โ”€ tests
    โ”œโ”€โ”€ __init__.py
    โ””โ”€โ”€ test_basics.py

์„ค์ • ์˜ต์…˜

  • ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ์ข…์ข… ์—ฌ๋Ÿฌ ์„ค์ •๊ฐ’์ด ํ•„์š”ํ•˜๋‹ค.
  • ๊ฐœ๋ฐœ, ํ…Œ์ŠคํŠธ, ์ œํ’ˆํ™” ๊ณผ์ • ์ค‘์— ์„œ๋กœ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋Œ€ํ‘œ์ ์ด๋‹ค.
import os
basedir = os.path.abspath(os.path.dirname(__file__))

class Config:
    SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string'
    MAIL_SERVER = os.environ.get('MAIL_SERVER', 'smtp.googlemail.com')
    MAIL_PORT = int(os.environ.get('MAIL_PORT', '587'))
    MAIL_USE_TLS = os.environ.get('MAIL_USE_TLS', 'true').lower() in \
        ['true', 'on', '1']
    MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
    MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
    FLASKY_MAIL_SUBJECT_PREFIX = '[Flasky]'
    FLASKY_MAIL_SENDER = 'Flasky Admin <flasky@example.com>'
    FLASKY_ADMIN = os.environ.get('FLASKY_ADMIN')
    SQLALCHEMY_TRACK_MODIFICATIONS = False

    @staticmethod
    def init_app(app):
        pass

class DevelopmentConfig(Config):
    DEBUG = True
    SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or \
        'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')

class TestingConfig(Config):
    TESTING = True
    SQLALCHEMY_DATABASE_URI = os.environ.get('TEST_DATABASE_URL') or \
        'sqlite://'

class ProductionConfig(Config):
    SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
        'sqlite:///' + os.path.join(basedir, 'data.sqlite')

config = {
    'development': DevelopmentConfig,
    'testing': TestingConfig,
    'production': ProductionConfig,

    'default': DevelopmentConfig
}
  • Config class๋Š” ๋ชจ๋“  ์„ค์ •์— ์‚ฌ์šฉ๋˜๋Š” ๊ณตํ†ต๊ฐ’๋“ค์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋‹ค.
  • ์ข€ ๋” ํ˜ธํ™˜์„ฑ์žˆ๊ณ  ์•ˆ์ •ํ•œ ์„ค์ •์„ ํ•˜๋ ค๋ฉด ํ™˜๊ฒฝ ๋ณ€์ˆ˜์—์„œ ์˜ต์…˜์œผ๋กœ ์ž„ํฌํŠธํ•ด์•ผ ํ•œ๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค๋ฉด SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string'
  • SQLALCHEMY_DATABASE_URI ๋ณ€์ˆ˜๋Š” ๊ฐ ์„ค์ •์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๊ฐ’์œผ๋กœ ์„ค์ •๋˜์–ด ์žˆ๋‹ค. ์„œ๋กœ ๋‹ค๋ฅธ DB ํ™˜๊ฒฝ์—์„œ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค.
  • init_app() ํด๋ž˜์Šค ๋ฉ”์†Œ๋“œ๋Š” ์„ค์ •์— ๋”ฐ๋ฅธ ์ดˆ๊ธฐํ™” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ด์ค€๋‹ค.

์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ํŒจํ‚ค์ง€

app
โ”œโ”€โ”€ __init__.py
โ”œโ”€โ”€ email.py
โ”œโ”€โ”€ main
โ”‚ย ย  โ”œโ”€โ”€ __init__.py
โ”‚ย ย  โ”œโ”€โ”€ errors.py
โ”‚ย ย  โ”œโ”€โ”€ forms.py
โ”‚ย ย  โ””โ”€โ”€ views.py
โ”œโ”€โ”€ models.py
โ”œโ”€โ”€ static
โ”‚ย ย  โ””โ”€โ”€ favicon.ico
โ””โ”€โ”€ templates
    โ”œโ”€โ”€ 404.html
    โ”œโ”€โ”€ 500.html
    โ”œโ”€โ”€ base.html
    โ”œโ”€โ”€ index.html
    โ””โ”€โ”€ mail
        โ”œโ”€โ”€ new_user.html
        โ””โ”€โ”€ new_user.txt
  • app ํŒจํ‚ค์ง€์—๋Š” ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ฝ”๋“œ, ํ…œํ”Œ๋ฆฟ, ์ •์ ํŒŒ์ผ, ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋ชจ๋ธ, ์ด๋ฉ”์ผ ์ž‘์—… ์ฝ”๋“œ ๋“ฑ์ด ์กด์žฌํ•œ๋‹ค.

๋ธ”๋ฃจํ”„๋ฆฐํŠธ

๋ธ”๋ฃจํ”„๋ฆฐํŠธ๋ฅผ ๊ฐ€์ง„ ๋ชจ๋“ˆํ™”๋œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ - Flask 0.11-dev documentation

  • ๋Œ€๊ทœ๋ชจ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ตฌ์กฐ๋ฅผ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•œ Flask์˜ ๊ธฐ๋Šฅ
  • ๋ธ”๋ฃจํ”„๋ฆฐํŠธ๋Š” ๋Œ€ํ˜• ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋™์ž‘ํ•˜๋Š” ๋ฐฉ์‹์„ ๋‹จ์ˆœํ™”ํ•œ๋‹ค
  • ๋ธ”๋ฃจํ”„๋ฆฐํŠธ์˜ ๊ธฐ๋ณธ ๊ฐœ๋…์€ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋ธ”๋ฃจํ”„๋ฆฐํŠธ์ด ๋“ฑ๋ก๋  ๋•Œ ์‹คํ–‰ํ•  ๋™์ž‘์„ ๊ธฐ๋กํ•œ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.
  • ํ”Œ๋ผ์Šคํฌ๋Š” ์š”์ฒญ์„ ๋ณด๋‚ด๊ณ  ํ•˜๋‚˜์˜ ๋์ ์—์„œ ๋‹ค๋ฅธ ๊ณณ์œผ๋กœ URL์„ ์ƒ์„ฑํ•  ๋•Œ ๋ทฐ ํ•จ์ˆ˜์™€ ๋ธ”๋ฃจํ”„๋ฆฐํŠธ์˜ ์—ฐ๊ด€์„ ๋งบ๋Š”๋‹ค.

์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ํŒฉํ† ๋ฆฌ

  • ์ผ๋ฐ˜์ ์ธ ํŒจํ„ด์€ ์ฒญ์‚ฌ์ง„์„ ์ž„ํฌํŠธํ•  ๋•Œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
  • ํ•˜์ง€๋งŒ ์ด ๊ฐ์ฒด์˜ ์ƒ์„ฑ์„ ํ•จ์ˆ˜๋กœ ์˜ฎ๊ธด๋‹ค๋ฉด, ๋‚˜์ค‘์— ์ด ๊ฐ์ฒด์— ๋Œ€ํ•œ ๋ณต์ˆ˜ ๊ฐœ์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

์‚ฌ์šฉ์ž ์ธ์ฆ

ํŒจ์Šค์›Œ๋“œ ๋ณด์•ˆ

  • ํŒจ์Šค์›Œ๋“œ๋ฅผ ์ €์žฅํ•  ๋•Œ๋Š” ํ•ด์‹ฑ(hashing)์„ ์‚ฌ์šฉํ•œ๋‹ค

  • ์ฆ‰, ํŒจ์Šค์›Œ๋“œ๋ฅผ ์žˆ๋Š” ๊ทธ๋ž˜๋„ DB์— ์ €์žฅํ•˜๋Š” ๊ฒƒ์ด์•„๋‹Œ ํ•ด์‹ฑ์„ ํ†ตํ•ด ๋ณ€ํ™˜๋œ ๋ฌธ์ž์—ด์„ ์ €์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

  • ์˜ˆ๋ฅผ ๋“ค์–ด๋ณด์ž.

    Password : "applebanana" โ†’ [Hash function] โ†’ DB : "AE21-00FF-E112"

    [๋กœ๊ทธ์ธ ๊ณผ์ •]

    1. ์‚ฌ์šฉ์ž๊ฐ€ "applebanana"๋กœ password๋ฅผ ์ž…๋ ฅ
    2. hash function์„ ํ†ตํ•ด์„œ ๋ณ€ํ™˜๋œ ๊ฐ’์„ ํš๋“
    3. ๋ณ€ํ™˜๋œ ๊ฐ’์„ ๊ฐ€์ง€๊ณ  DB์— ์ €์žฅ๋œ ๊ฐ’๊ณผ ๋น„๊ต

    [๋ณด์•ˆ์„ฑ]

    • "applebanana" โ†’ hash โ†’ "AE21-00FF-E112" (Possible)
    • "AE21-00FF-E112" โ†’ hash โ†’ "applebanana" (Impossible)
    • ์ฆ‰, ๋ณ€ํ™˜๋œ ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ๋ณ€ํ™˜๋˜๊ธฐ ์ „ ๊ฐ’์„ ์ฐพ๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

Werzeung [์›”-์ ]

  • Werkzeug [์›”์ ] ์€ WSGI web application library์ด๋‹ค.

  • ์›”์ ์˜ ๋ณด์•ˆ ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ณด์•ˆ ํŒจ์Šค์›Œ๋“œ ํ•ด์‹ฑ์„ ํŽธํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

    generate_password_hash(password, method=pbkdf2:sha1, salt_lenth=8)

    โ†’ ์ด ํ•จ์ˆ˜๋Š” plain-text์ธ password๋ฅผ ๋ฐ›์•„์„œ password hash๋ฅผ returnํ•œ๋‹ค.

    โ†’ method, length๋Š” ๊ทธ๋Œ€๋กœ ๋‘๊ณ  ์‚ฌ์šฉํ•ด๋„ ๋œ๋‹ค.

    check_password_hash(hash, password)

    โ†’ DB์—์„œ ์ถ”์ถœํ•œ password hash ์™€ ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ password๋ฅผ ๋น„๊ตํ•œ๋‹ค.

    โ†’ True๊ฐ€ ๋ฐ˜ํ™˜๋˜๋ฉด ํŒจ์Šค์›Œ๋“œ๊ฐ€ ์ผ์น˜ํ•œ๋‹ค๋Š” ๋œป์ด๋‹ค.

์ธ์ฆ ๋ธ”๋ฃจํ”„๋ฆฐํŠธ ์ƒ์„ฑ

  • ์‚ฌ์šฉ์ž ์ธ์ฆ๊ณผ ๊ด€๋ จ๋œ ๋ผ์šฐํŠธ๋Š” auth ๋ธ”๋ฃจํ”„๋ฆฐํŠธ์— ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ธฐ๋Šฅ์˜ ๋‹ค๋ฅธ ์ง‘ํ•ฉ์„ ์œ„ํ•ด ๋‹ค๋ฅธ ๋ธ”๋ฃจํ”„๋ฆฐํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์ฝ”๋“œ๋ฅผ ๊ตฌ์กฐ์ ์œผ๋กœ ๋ณด๊ธฐ ์ข‹๊ฒŒ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.
  1. ๊ฐ™์€ ์ด๋ฆ„์˜ ํŒจํ‚ค์ง€์—์„œ ํ˜ธ์ŠคํŠธํ•œ๋‹ค.

    app/auth/__init__.py ์—์„œ auth ๋ธ”๋ฃจํ”„๋ฆฐํŠธ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค

    from flask import Blueprint
    
    auth = Blueprint('auth', __name__)
    
    from . import views
  2. views.py ์—์„œ auth๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

    app/auth/views.py

    from flask import render_template
    from . import auth
    
    @auth.route('/login')
    def login():
    	return render_template('auth/login.html')

    ์–ธ๋œป๋ณด๋ฉด ์ˆœํ™˜ ๊ตฌ์กฐ์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ __init__.py ๋Š” auth ํŒจํ‚ค์ง€๊ฐ€ ์ž„ํฌํŠธ ๋ ๋•Œ ํ•œ๋ฒˆ ์‹คํ–‰๋œ๋‹ค.

    ๊ทธ๋ฆฌ๊ณ  [views.py](http://views.py) ๊ฐ€ ์‹คํ–‰๋˜๋ฉด์„œ __init__.py ์—์„œ ์„ ์–ธํ•œ auth blueprint๋ฅผ ๋ถˆ๋Ÿฌ์™€์„œ ์‚ฌ์šฉํ•œ๋‹ค.

    render_template('auth/login.html') ์—์„œ ๊ด„ํ˜ธ ์•ˆ์˜ ๊ฒฝ๋กœ๋Š” app/templates/auth/login.html ์ด๋‹ค. render_tamplate() ํ•จ์ˆ˜๋Š” ๋จผ์ € ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์šฉ์œผ๋กœ ์„ค์ •๋œ ํ…œํ”Œ๋ฆฟ ํ’€๋”๋ฅผ ์šฐ์„  ๊ฒ€์ƒ‰ํ•œ๋‹ค.

  3. ๋ธ”๋ฃจํ”„๋ฆฐํŠธ๋ฅผ ๋ถ€์ฐฉํ•œ๋‹ค.

    app/__inti__.py

    def create_app(config_name):
    	# ...
    	from .auth import auth as auth_blueprint
    	app.register_blueprint(auth_blueprint, url_prefix='/auth')
    
    	return app
  • url_prefix ์ธ์ˆ˜๋Š” ์˜ต์…˜์ด๋‹ค.
  • url_prefix ์ธ์ˆ˜ ์˜ต์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ํ•ด๋‹น ๋ธ”๋ฃจํ”„๋ฆฐํŠธ์— ์ •์˜๋œ ๋ชจ๋“  ๋ผ์šฐํŠธ๋Š” ์ฃผ์–ด์ง„ ์ ‘๋‘์–ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋“ฑ๋ก๋œ๋‹ค
  • ๋”ฐ๋ผ์„œ /login ๋ผ์šฐํŠธ๋Š” /auth/login ๋กœ ๋“ฑ๋ก๋˜์—ˆ๊ณ  http://localhost:500/auth/login ์—์„œ ์ ‘์†์ด ๊ฐ€๋Šฅํ•ด ์ง„๋‹ค.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages