#Flask #Jinja #Template Engine #Bootstrap #웹 어플리케이션
🏆 학습 목표
파이썬을 사용해 웹 어플리케이션을 작성할 수 있도록 도와주는 자유로운 마이크로 웹 프레임워크(Micro Web Framework)
마이크로
: 프레임워크를 간결하게 유지하고 확장할 수 있도록 만들었다는 뜻간결하다
: 플라스크를 이용하면 파일 하나로 구성된 짧은 코드만으로도 완벽하게 동작하는 웹 프로그램을 만들 수 있다.확장성 있는 설계
: 장고와 달리 플라스크에는 폼(form), 데이터베이스(database)를 처리하는 기능이 없다. 플라스크는 확장 모듈을 사용해 그때그때 개발자가 필요한 확장 모듈을 포함해 가며 개발하기 때문에 실제로 프로젝트의 무게가 가볍다.자유로운
: 프레임워크는 대부분 규칙이 복잡하며 개발자는 그 규칙을 반드시 따라야 한다. 플라스크도 마찬가지이지만 비교적 최소한의 규칙만 있어 개발의 자유도가 다른 프레임워크보다 높다.웹 (어플리케이션) 프레임워크?
웹 어플리케이션을 개발할 수 있도록 웹 서비스(Web Service) 나 웹 API(Web API)등을 제공하고 웹 개발과 배포를 할 수 있는 특정 방법을 제공한다. 쉽게 말해 뭔가를 만들어낼 수 있는 도구 모음을 제공한다고 보면 된다.
LIBRARY vs FRAMEWORK
- 공통점 : 둘 다 다른 누군가가 쓴 코드로 우리의 프로젝트를 위해 가져다 쓰는 것
- 차이점 : 누가 누구를 컨트롤 하는가!
- LIBRARY : 내가 필요할 때 가져다 쓰는 것으로 쉽게 다른 것으로 대체 가능(내가 control)
- FRAMEWORK : 규칙을 따라야만 작동이 잘 됨(프레임워크가 나를 control)
(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
: localhost5000
: 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이 우리가 작성한 함수를 실행시키는지 알려준다.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 인터프리터를 이용해 단일 모듈을 직접 실행한다면 이 문장은 우리가 실행한 서버가 현재 동작되는 유일한 서버라는 것을 보장한다.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()
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')
맞춤형 웹 페이지를 자동으로 생산할 수 있도록 도와주는 웹 템플렛 엔진(Web Template Engine)
(Jinja 2 는 Flask 설치와 함께 오기 때문에 추가 설치가 따로 필요 없다.)
ex) 검색창에서 특정 검색어를 찾았을 때에 해당 검색어를 기준으로 결과 페이지가 보여지는 것
중괄호 '{ }'
### title이라는 변수를 <h1> 태그 안에 넣어서 렌더링하는 방식 <body> <h1>{{ title }}</h1> </body>
- 이외 표현
{% ... %}
: 구문에 사용이 됩니다 (if, for 등).{{ ... }}
: 템플렛 결과(html) 에 출력할 표현입니다 (변수 등).{# ... #}
: 주석 처리할 때 사용됩니다.
render_template
함수 사용### 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>
객체 태그
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 %}
{% extends "부모 템플렛 이름"%}
{% block 블록이름 %}
... {% endblock %}
(이름으로 상속 구별)Jinja 는 상속에서 부모와 자식 구분이 없어 한번 상속을 하면 부모 파일의 모든 것을 상속하게 되므로 자식 HTML 파일은 부모의 파일 내용 이후에 작성이 된다. 이러한 부분을 극복하기 위해 Jinja 는
block
을 사용한다.
super
로 접근 가능super.super()
로도 접근 가능)### 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 %}
© 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 %}
(... 생략 ...)
HTML을 빠르고 예쁘게 꾸밀 수 있는 도구로 최소한으로 알고 있어야 하는 HTML, CSS 의 지식으로 손쉽게 사용할 수 있다.
부트스트랩은 설치 없이 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 -->
부트스트랩 문서
위 컴포넌트들은 부트스트랩에서 사전에 스타일링 등 설정을 거친 상태이기 때문에 HTML 만 복사해서 사용할 수 있습니다.
- 네비게이션 바
- 손쉽게 웹 페이지 제일 상단에 메뉴를 생성할 수 있다.
- 다양한 색과 형태를 지원하고 메뉴 버튼들도 드롭다운, 클릭, 활성화 (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> (... 생략 ...)
(... 생략 ...) <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 엔드포인트 설정하기