n332_Flask

ssu_hyun·2021년 10월 3일
0

[codestates] AI Bootcamp

목록 보기
34/62

#Flask #Jinja #Template Engine #Bootstrap #웹 어플리케이션

🏆 학습 목표

  • Flask에 대해서 설명할 수 있어야 합니다.
  • Flask 서버를 띄울 수 있어야 합니다.
  • Jinja 템플렛을 사용할 수 있어야 합니다.
  • Bootstrap 을 사용할 수 있어야 합니다.



Flask

파이썬을 사용해 웹 어플리케이션을 작성할 수 있도록 도와주는 자유로운 마이크로 웹 프레임워크(Micro Web Framework)

  • 마이크로 : 프레임워크를 간결하게 유지하고 확장할 수 있도록 만들었다는 뜻
    • 간결하다 : 플라스크를 이용하면 파일 하나로 구성된 짧은 코드만으로도 완벽하게 동작하는 웹 프로그램을 만들 수 있다.
    • 확장성 있는 설계 : 장고와 달리 플라스크에는 폼(form), 데이터베이스(database)를 처리하는 기능이 없다. 플라스크는 확장 모듈을 사용해 그때그때 개발자가 필요한 확장 모듈을 포함해 가며 개발하기 때문에 실제로 프로젝트의 무게가 가볍다.
  • 자유로운 : 프레임워크는 대부분 규칙이 복잡하며 개발자는 그 규칙을 반드시 따라야 한다. 플라스크도 마찬가지이지만 비교적 최소한의 규칙만 있어 개발의 자유도가 다른 프레임워크보다 높다.
  • Jinja, Werkzeug, Flask-SQLAlchemy 등 다양한 패키지들과 라이브러리들이 존재
  • Flask 홈페이지
  • Flask-docs-kr

웹 (어플리케이션) 프레임워크?
웹 어플리케이션을 개발할 수 있도록 웹 서비스(Web Service) 나 웹 API(Web API)등을 제공하고 웹 개발과 배포를 할 수 있는 특정 방법을 제공한다. 쉽게 말해 뭔가를 만들어낼 수 있는 도구 모음을 제공한다고 보면 된다.

LIBRARY vs FRAMEWORK

  • 공통점 : 둘 다 다른 누군가가 쓴 코드로 우리의 프로젝트를 위해 가져다 쓰는 것
  • 차이점 : 누가 누구를 컨트롤 하는가!
    • LIBRARY : 내가 필요할 때 가져다 쓰는 것으로 쉽게 다른 것으로 대체 가능(내가 control)
    • FRAMEWORK : 규칙을 따라야만 작동이 잘 됨(프레임워크가 나를 control)



Flask 시작하기

(CLI)
### 가상환경 생성 및 활성화
conda create -n flask python=3.8
conda activate flask

### Flask 설치
pip install flask
# 설치 확인 : pip list

### Flask 어플리케이션 저장 폴더 생성(주로 application 이름으로 지정)
mkdir flask_app

### 폴더 내에 __init__.py 생성
cd flask_app
touch __init__.py

### Flask 웹 어플리케이션 생성
from flask import Flask
app = Flask(__name__)  #해당 어플리케이션 이름 지정


### (CLI) 실행 명령은 반드시 "프로젝트 폴더 상위 디렉토리"에서!
FLASK_APP=flask_app flask run
#FLASK_APP=폴더이름 flask run

[위 코드 실행 시 보여지는 code]

* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) 의 의미

  • http://127.0.0.1:5000/ : 어플리케이션에 접속할 수 있는 주소
  • 127.0.0.1 : localhost
  • 5000 : 5000번 포트에서 작동하고 있음
  • (Press CTRL+C to quit) : 어플리케이션 종료 방법

라우팅

# __init__.py

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello World!'
    
if __name__ == '__main__':
    app.run()

@app.route('/')

  • route() 데코레이터를 사용해 Flask에게 어떤 URL이 우리가 작성한 함수를 실행시키는지 알려준다.
  • 어플리케이션의 루트 주소('/')에 접속했을 때에 실행하라는 뜻
  • URL에 따라 실행하게 될 함수를 지정하는 역할
  • 주소( 127.0.0.1:5000/ )가 어떻게 끝나는지를 말하는 엔드포인트(endpoint) 설정
    • 엔드포인트 /about : 127.0.0.1:5000/about에 접속할 수 있게 됨
    • 기본적으로 기본 URL 뒤에는 슬래쉬 ( / ) 가 붙어야하기 때문에 ( / ) 없이 그저 @app.route('about') 와 같은 설정은 Flask 를 실행할 때에 에러를 일으킨다.
  • 실행 함수 : index
  • if __name__ == '__main__':app.run() : 어플리케이션으로 시작되는지, 혹은 모듈로 임포트되는지에 따라 이름이 달라지므로 소스파일을 모듈이 아닌 python 인터프리터를 이용해 단일 모듈을 직접 실행한다면 이 문장은 우리가 실행한 서버가 현재 동작되는 유일한 서버라는 것을 보장한다.
  • CLI에서 FLASK_APP=flask_app flask run 으로 실행해서 마찬가지로 127.0.0.1:5000/ 주소에 접속하게 되면 웹 페이지에 index실행값 출력(Hello World!)

라우트 기능들

HTTP Request 메소드

  • 기본 허용 HTTP Request 메소드 : GET, HEAD, OPTIONS
  • 이외 HTTP Request 메소드 : 데코레이터 함수에 methods라는 인수 추가
# methods를 통해 POST, PUT, PATCH, DELETE 등 다른 메소드들 통과 설정
@app.route('/', methods=['POST', 'GET']) #HEAD, OPTIONS 사용 불가하므로 추가로 적어줘야 함
def index():
    ...

세부 엔드포인트
URL을 세부적으로 지정하고 주소에서 값을 전달할 수도 있다.

# Welcome to Index { 번호 } 리턴
@app.route('/index/<num>')
def index_number(num):
    return f'Welcome to Index {int(num)}'  
  • 라우트 설정 시 <>를 통해 엔드포인트에 어떠한 변수를 받겠다고 설정할 수 있다.
    ( 꺽쇠 안 : 변수의 이름 지정 / 함수 : 변수를 그대로 받아 사용 )
  • 만약에 index/hello를 넘겨주면 hello가 값이 된다.
  • int(num) : 기본적으로 URL을 통해 들어오게 되는 값은 문자열 타입이므로 변수값의 타입 변환도 신경 써주어야 한다.
  • @app.route('/index/<int:num>') : 처음 받을 때부터 타입 지정
  • defaults : 변수가 주어지지 않을 경우 대비
# 숫자가 안 주어지면 0을 기본으로 설정해주는 방법 = defaults
@app.route('/index/', defaults={ 'num' : 0 })
@app.route('/index/<num>')
def index_number(num):
    return f'Welcome to Index {int(num)}'

블루프린트
여러 개의 라우트를 한 곳에 묶어둘 수 있는 기능
(라우트들을 기능별로 나눠 블루프린트 기능 사용)

### routes 디렉토리 + 파이썬 파일
#(CLI)
cd flask_app # flask_app 하위 폴더로 생성
mkdir routes # routes 디렉토리 생성
cd routes # routes 디렉토리 내 파이썬 파일 생성
touch user_routes.py # user_routes 파이썬 파일 생성
### user_routes.py
from flask import Blueprint
bp = Blueprint('main', __name__, url_prefix='/main')
@bp.route('/') #=> 127.0.0.1:5000 + '/main' + '/' => 127.0.0.1:5000/main
def index():
    return 'Welcome to Main Index'

Blueprint() 인수

  • 'user' : 블루프린트의 명칭(블루프린트가 flask 자체에서 인지할 때 이름)
  • __name__ : 블루프린트의 import 이름 (블루프린트 가져올 때 import 이름)
  • url_prefix='/user' : URL 접두어 설정 (해당 블루프린트의 라우트는 URL 앞에 '/user' 가 자동으로 붙게 됩니다.)
  • __init__.py파일에서 해당 파일 불러오기
# __init__.py
from flask import Flask
from flask_app.routes import user_routes #추가
app = Flask(__name__)
app.register_blueprint(user_routes.bp) #추가
@app.route('/')
def index():
    return 'Hello World!'
(... 생략  ...)

Application Factory

  • 프로젝트가 커지고 파일들이 많아지면 import를 사용할 일이 많아지므로 이러한 circular import를 피하기 위해 사용
  • 말 그대로 어플리케이션을 만드는 '공장'을 세우는 것
  • 글로벌한 컨텍스트가 아닌 함수 안에 어플리케이션 생성하는 것
# __init__.py
from flask import Flask
def create_app():
    app = Flask(__name__)
    from yourapplication.views.admin import admin
    from yourapplication.views.frontend import frontend
    app.register_blueprint(admin)
    app.register_blueprint(frontend)
    return app
if __name__ == "__main__":
  app = create_app()
  app.run()



Jinja template

Flask 의 HTML 렌더링

render_template메소드
Flask에서 html 파일들을 불러올 수 있는 방법

프로젝트 폴더 내에 templates(html 파일 보관)라는 이름의 폴더를 기본 경로로 설정
(**templates 폴더 내에 또다른 폴더에 있다해도 경로는 templates 폴더 기준으로 잡아줘야 한다.)

### 'index.html' 보여주는 방법
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
  return render_template('index.html')
(... 생략 ...)

### main 폴더에 'main.html' 파일이 존재할 경우
@app.route('/main')
def main():
  return render_template('main/main.html')

Jinja

맞춤형 웹 페이지를 자동으로 생산할 수 있도록 도와주는 웹 템플렛 엔진(Web Template Engine)
(Jinja 2 는 Flask 설치와 함께 오기 때문에 추가 설치가 따로 필요 없다.)

ex) 검색창에서 특정 검색어를 찾았을 때에 해당 검색어를 기준으로 결과 페이지가 보여지는 것

Jinja expressions

중괄호 '{ }'

### title이라는 변수를 <h1> 태그 안에 넣어서 렌더링하는 방식
<body>
  <h1>{{ title }}</h1>
</body>
  • 이외 표현
    • {% ... %} : 구문에 사용이 됩니다 (if, for 등).
    • {{ ... }} : 템플렛 결과(html) 에 출력할 표현입니다 (변수 등).
    • {# ... #} : 주석 처리할 때 사용됩니다.

Flask > Jinja 변수 넘기기

  • render_template 함수 사용
  • 인수로 변수를 넘겨주면 html 파일에서 받아 사용할 수가 있다.
### python 파일
@app.route('/')
def index():
    apple = 'apple'
    return render_template('index.html', apple=apple)  # 변수 추가
<!--html 파일-->
<html>
    <head>
      <title>
        HTML Page
      </title>
    </head>
    <body>
      <h1>Writing NEW HTML...{{ apple }}</h1>
    </body>
   </html>
### python 파일
@app.route('/')
def index():
    apple = 'red'
    apple_count = 10
    return render_template('index.html', fruit_color=apple, number=apple_count)
<!--html 파일-->
<body>
  <h2>Apple is {{ fruit_color }}</h2>
  <h2>{{ apple_count }} 개의 과일이 있습니다.</h2>
</body>
### python 파일
@app.route('/<item>')
def index(item):
    return render_template('index.html', item=item)  # /뒤 item 입력하면 그 값 그대로 출력
<!--html 파일-->
<html>
    <head>
      <title>
        HTML Page
      </title>
    </head>
    <body>
      <h1>Writing NEW HTML...{{ item }}</h1>
    </body>
   </html>

Jinja Functions

객체 태그
Flask 어플리케이션에서부터 파이썬 변수를 넘겨 받아 사용할 수 있다.

# var=[1, 2, 3]
{{ var }}
# fruits = { 'apple' : 'red', 'banana' : 'yellow' }
{{ fruits.apple }}
# vegetables = [ 'cucumber', 'spinach' ]
{{ vegetables[0] }}

if 구문

{% if True %}
<h1>It is True</h1>
{% endif %}
# 'else' 나 'elif'와 같은 조건 추가
{% if True %}
<h1>It is True</h1>
{% elif False %}
<h1>It is False</h1>
{% else %}
<h1>It is not True</h1>
{% endif %}

for 구문

# item_list = ['book', 'keyboard', 'window']일 때
{% for item in item_list %}
<p>{{ item }}</p>
{% endfor %}
loop 속성설명
loop.index반복 순서 1부터 1씩 증가
loop.index0반복 순서 0부터 1씩 증가
loop.first반복 순서가 처음일 경우 True 아니면 False
loop.last반복 순서가 마지막일 경우 True 아니면 False

=> loop을 통해 추가 정보를 얻을 수 있다.

{% for item in item_list %}
<p>인덱스 : {{ loop.index0 }}, 이름 : {{ item }}</p>
{% endfor %}

📌Jinja 상속

  • 반복적으로 사용되는 부분들을 하나로 묶어서 사용할 수 있게끔 해주는 기능
  • 부모 템플렛의 경로를 자식 템플렛 파일의 첫줄에 적어주게 되면 Jinja 의 템플렛 엔진이 해당 파일을 이용한다.
    • {% extends "부모 템플렛 이름"%}
  • 상속된 파일에서 변경할 수 있는 일종의 HTML '블록'들 지정
    • {% block 블록이름 %} ... {% endblock %} (이름으로 상속 구별)

      Jinja 는 상속에서 부모와 자식 구분이 없어 한번 상속을 하면 부모 파일의 모든 것을 상속하게 되므로 자식 HTML 파일은 부모의 파일 내용 이후에 작성이 된다. 이러한 부분을 극복하기 위해 Jinja 는 block 을 사용한다.

  • 상속은 여러 번 할 수 있다.
  • 같은 경로에 생성해준다.(templates 폴더 기준)
  • 자식이 부모 블록의 내용을 사용할 경우 :super로 접근 가능
    (2번 상속된 파일이 가장 상위 부모의 내용을 접근하기 위해서는 super.super() 로도 접근 가능)
  • Jinja 의 상속 기능은 편리하나 전부를 가져오거나 안 가져오거나 라는 두 가지 선택 밖에 없어 주의해야 한다.
### base.html
<!DOCTYPE html>
<html lang="en">
<head>
    {% block head %} 
    <title>{% block title %}{% endblock %} - My Webpage</title>
    {% endblock %}
</head>
<body>
    <div id="content">{% block content %}{% endblock %}</div>
    <div id="footer">
        {% block footer %}
        &copy; Copyright 2008 by <a href="http://domain.invalid/">you</a>.
        {% endblock %}
    </div>
</body>
</html>


### child.html
{% extends "base.html" %}  # base.html 파일 내용 상속(첫 줄에 위치해야함)
{% block title %}Index{% endblock %}
{% block head %}
    {{ super() }}
{% endblock %}
{% block content %}
    <h1>Index</h1>
    <p class="important">
      Welcome to my awesome homepage.
    </p>
{% endblock %}


### super()
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
    {{ super() }}
    <h1>자식 블록에서 출력한 h1 입니다.</h1>
{% endblock %}

(... 생략 ...)



Boostrap

HTML을 빠르고 예쁘게 꾸밀 수 있는 도구로 최소한으로 알고 있어야 하는 HTML, CSS 의 지식으로 손쉽게 사용할 수 있다.


install

부트스트랩은 설치 없이 HTML 헤더 부분에 자바스크립트와 CSS 링크만 넣어줘도 동작을 한다.

<!DOCTYPE html>
<html>
  <head>
    <!-- CSS only -->  
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">

    <!-- JavaScript Bundle with Popper -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script>
  </head>
  <body>
  </body>
</html>
  • <!-- CSS only -->
    : 부트스트랩의 스타일만 가지고 올 때 사용 (모양새만 부트스트랩에서부터 가지고 올 수 있는 링크)
  • <!-- JavaScript Bundle with Popper -->
    : 드롭다운 메뉴나 펼쳐지는 메뉴 등 동작이 필요한 컴포넌트가 있어야 할 때

컴포넌트(Component)

부트스트랩 문서
위 컴포넌트들은 부트스트랩에서 사전에 스타일링 등 설정을 거친 상태이기 때문에 HTML 만 복사해서 사용할 수 있습니다.

Navbar

  • 네비게이션 바
  • 손쉽게 웹 페이지 제일 상단에 메뉴를 생성할 수 있다.
  • 다양한 색과 형태를 지원하고 메뉴 버튼들도 드롭다운, 클릭, 활성화 (active), 비활성화 (in-active) 등 다양한 상태와 기능들을 지원
(... 생략 ...)
<nav class="navbar navbar-expand-lg navbar-light bg-light">
  <div class="container-fluid">
    <a class="navbar-brand" href="#">Navbar</a>
    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav me-auto mb-2 mb-lg-0">
        <li class="nav-item">
          <a class="nav-link active" aria-current="page" href="#">Home</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" href="#">Link</a>
        </li>
        <li class="nav-item dropdown">
          <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
            Dropdown
          </a>
          <ul class="dropdown-menu" aria-labelledby="navbarDropdown">
            <li><a class="dropdown-item" href="#">Action</a></li>
            <li><a class="dropdown-item" href="#">Another action</a></li>
            <li><hr class="dropdown-divider"></li>
            <li><a class="dropdown-item" href="#">Something else here</a></li>
          </ul>
        </li>
      </ul>
      <form class="d-flex">
        <input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
        <button class="btn btn-outline-success" type="submit">Search</button>
      </form>
    </div>
  </div>
</nav>
(... 생략 ...)

Buttons

(... 생략 ...)
<button type="button" class="btn btn-primary">Primary</button>
<button type="button" class="btn btn-secondary">Secondary</button>
<button type="button" class="btn btn-success">Success</button>
<button type="button" class="btn btn-danger">Danger</button>
<button type="button" class="btn btn-warning">Warning</button>
<button type="button" class="btn btn-info">Info</button>
<button type="button" class="btn btn-light">Light</button>
<button type="button" class="btn btn-dark">Dark</button>
<button type="button" class="btn btn-link">Link</button>
(... 생략 ...)



[REFERENCE]

점프 투 플라스크
flask jsonify 와 json.dumps의 차이
Flask jsonify
Python Flask 엔드포인트 설정하기

0개의 댓글