플라스크는 Micro Web Framework 이다. 웹 프레임워크는 웹 애플리케이션을 개발할 수 있도록 웹 서비스나 웹 API등을 제공하고 웹 개발과 배포를 할 수 있는 특정 방법을 제공한다. 뭔가를 할 수 있는 도구모음을 제공한다고 생각하면 된다. 여기에 micro가 붙었으니 도구 모음들이 최소한의 크기로 줄여진거라고 할 수 있다.
기본적으로 프레임워크에 따라 패키지와 라이브러리 등 모듈들의 컬렉션이 있어 개발이 수월할 수 있도록 도와준다. Jinja, Werkzeug, Flask-SQLAlchemy 등 다양한 패키지들과 라이브러리들이 존재한다.
__init__.py
)from flask import Flask
app = Flask(__name__)
# 라우트 추가
@app.route('/') #=> 기본URL + / => 127.0.0.1:5000/
def index():
return 'Hello World!'
@app.route('/')
는 애플리케이션의 루트 주소에 접속했을 때 실행하라는 뜻. URL에 따라 실행하게 될 함수를 지정하는 역할.
즉, 엔드포인트(endpoint)를 설정하게 되는데 엔드포인트는 주소가 어떻게 끝나는지를 말하고 있는 것.
추가로,
return 'Hello World!', 201
와 같이 return 값과 함께 숫자를 넘겨주면 상태코드를 함께 보내줄 수 있다.
GET
, HEAD
, OPTIONS
3가지의 HTTP Request 메서드를 허용method
인자를 추가# GET과 POST 메서드 사용, HEAD, OPTIONS는 사용 불가
@app.route('/', methods=['POST', 'GET'])
def index():
...
@app.route('/index/<num>') # num이라는 변수 만들어 받게 됨
def index_number(num):
return 'Welcome to Index %i' % int(num)
# url을 통해 들어오게 되는 값은 문자열이기 때문에 숫자로 변환
@app.route('/index/' ,defaults={'num':0}) # num값이 주어지지 않은 경우 default설정
@app.route('/index/<int:num>') # 위에서 형변환
def index_number(num):
return 'Welcome to Index %i' % num
기능이 많아질수록 라우트도 많아진다. 그래서 이러한 라우트들을 하나의 파일로 모아서 사용하지 않고 기능별로 나눠서 블루프린트 기능을 사용한다. 블루프린트는 Flask에서 여러 개의 라우트를 한곳에 묶어둘 수 있는 기능이 있다.
routes(폴더) -> user_routes.py
# user_routes.py
from flask import Blueprint
# user : 블루프린트 명칭
# __name__ : 블루프린트 import 이름
# url어쩌고 : URL 접두어 설정
bp = Blueprint('user', __name__, url_prefix='/user')
@bp.route('/')
def index():
return 'User index page'
# __init__.py
from flask import Flask
from flask_app.routes import user_routes # import
app = Flask(__name__)
app.register_blueprint(user_routes.bp) #register_blueprint 메소드 사용
@app.route('/')
def index():
return 'Hello World!'
프로젝트가 커지고 파일들이 많아지면 import를 사용하는 일이 많아지게 된다. 이때 파이썬에서 circular import(순환참조)를 피하기 위해서 Flask에서는 애플리케이션 팩토리 패턴을 추천하고 있다. → 함수를 따로 만들어주는것!
# __init__.py
from flask import Flask
def create_app():
app = Flask(__name__)
# 내부에서 import
from flask_app.routes import user_routes
app.register_blueprint(user_routes.bp)
return app
if __name__ == "__main__":
app=create_app()
app.run()
이렇게 사용해도 위와 동일한 결과를 얻을 수 있다.
웹브라우저에서 웹 페이지를 볼 때는 HTML이라는 마크업 언어 사용. Flask에서도 HTML 파일들을 제공하면서 웹 페이지를 클라이언트에게 보여줄 수 있다. 그러나! 단순히 정해진 변경하지 않는 데이터와 페이지가 아니라 상황에 따라 다른 정보를 웹 페이지를 보여줘야 한다.
render_template
메소드를 사용해서 html 파일 불러올 수 있음
→ 'templates' 폴더를 기본 경로로 설정하고, 내부에 html 파일 모아두고 사용하기
<html>
<head>
<title>
New HTML Page
</title>
</head>
<body>
<h1>I am in templates folder</h1>
</body>
</html>
# __init__.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
만약 templates 폴더 내에 또 다른 폴더에 있어도 경로는 templates 폴더 기준으로 해야한다.
📂templates
-📂main
-main.html
을 사용하고자 한다면,render_template('main/main.html')
로 적용해준다.
Jinja는 웹 템플릿 엔진(Web Template Engine)으로 맞춤형 웹 페이지를 자동으로 생산할 수 있도록 도와준다. 검색창에서 특정 검색어를 찾았을 때에 해당 검색어를 기준으로 결과 페이지가 보이는 것처럼 말이다.
** 상황에 따라 다르게 보여줘야 하는 경우에 위와 같이 설정하는걸로는 수작업으로 일일히 바꿔줘야 될 수도 있다. 그래서 Jinja를 사용하면 좋다.
중괄호({})를 많이 사용한다. 변수를 사용할 때는 다음과 같이 사용한다.
<body>
<h1>{{title}}</h1> # title 변수를 <h1>에 넣어 렌더링
</body>
** 주로 활용되는 표현식
이전과 같이 render_template
함수를 사용할 수 있다. 해당 함수를 실행할 때에 인수로 변수를 넘겨주면 html 파일에서 받아 사용할 수 있다.
<html>
<head>
<title>
New HTML Page
</title>
</head>
<body>
<h1>fruit!</h1>
<h2>Apple is {{fruit_color}}</h2>
<h2>{{number}}개의 과일이 있습니다.</h2>
</body>
</html>
# __init__.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
apple = 'red'
apple_count = 10
return render_template('index.html', fruit_color=apple, number=apple_count)
🎈 자세한 설명은 Jinja 공식문서 참고
1. 객체 태그
- Flask 애플리케이션에서부터 파이썬 변수를 넘겨받아 사용
- 문자열, 숫자, 중첩된 딕셔너리, 리스트 등 사용가능
var = [1,2,3]
→ {{var}}
fruits = {'apple':'red','banana':'yellow'}
→ {{fruits.apple}}
vegetables = ['cucumber', 'spinach']
→ {{vegetables[0]}}
<html>
<head>
<title>
New HTML Page
</title>
</head>
<body>
<h2>사과 색은 {{fruit.apple}}입니다.</h2>
<h2></h2>
</body>
</html>
# __init__.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def index():
fruits = {'apple':'red','banana':'yellow'}
return render_template('index.html', fruit=fruits)
2. 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 %}
3. for 구문
# item_list 라는 리스트가 ['book', 'keyboard', 'window'] 일 때 for 로 순환
{% for item in item_list %}
<p>{{ item }}</p>
{% endfor %}
이때 각 반복마다 Jinja 에서 제공하는 loop 이라는 것을 활용해 추가 정보를 얻을 수 있다.
loop 속성 | 설명 |
---|---|
loop.index | 반복 순서 1부터 1씩 증가 |
loop.index0 | 반복 순서 0부터 1씩 증가 |
loop.first | 반복 순서가 처음일 경우 True 아니면 False |
loop.last | 반복 순서가 마지막일 경우 True 아니면 False |
{% for item in item_list %}
<p>인덱스 : {{ loop.index0 }}, 이름 : {{ item }}</p>
{% endfor %}
반복적으로 사용되는 부분들을 하나로 묶어서 사용할 수 있게끔 해주는 기능
{% extends %}
와 {% block %} ... {% endblock %}
을 사용
만약 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>
base.html
파일과 같은 경로에 생성한 새로운 child.html
파일에 base.html
파일 내용을 상속받고 싶다면 다음과 같이 작성한다.
{% extends "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 %}
이렇게 되면 자식 파일인 child.html
은 base.html
로부터 상속을 받게 된다. 상속은 첫 줄에 위치해야 한다. 그리고 상속은 여러 번도 가능하다.
만약에 경로가 다르면 반영해서 파일의 위치를 잘 적어두면 된다. Flask 를 사용할 때에는 templates 폴더를 기준으로 삼는다.
{% block %} 문구는 상속된 파일에서 변경할 수 있는 일종의 HTML '블록' 들을 지정해준다.
Jinja 는 상속에서 부모와 자식 구분이 없다. 그렇기 때문에 한번 상속을 하면 부모 파일의 모든 것을 상속하게 된다. 따라서 자식 HTML 파일에서 뭔가를 작성하게 된다면 부모의 파일 내용 이후에 작성이 된다. 그래서 이를 극복하기 위해 block을 사용하는데 여기엔 시작과 끝이 있어야 한다.
<head>
{% block head %}
<title>{% block title %}{% endblock %} - My Webpage</title>
{% endblock %}
</head>
'block' 이라는 단어 뒤에 'head' 단어가 붙어 있는데, 이것은 각 block 의 이름이다.
템플릿 엔진이 상속했을 때에 구별할 수 있도록 이름을 지어주는 것이다. 이 이름은 중복해서 사용하면 Jinja 에서 어떤 블록을 사용해야 하는지 모르게 되니 각각 지정해 주는게 좋다.
또한 자식 파일에서 부모 파일에서 사용한 블록을 사용하게 되면 새로 덮어쓰게 되어 부모 블록의 내용은 출력되지 않는다.
자식이 부모 블록의 내용을 사용하려면 파이썬과 비슷하게 super
를 통해서 사용 가능하다. 2번 상속된 파일이 가장 상위 부모의 내용을 접근하기 위해서는 super.super()
로도 접근 가능하다.
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ super() }}
<h1>자식 블록에서 출력한 h1 입니다.</h1>
{% endblock %}
이렇게 사용한다면 부모 블록의 내용과 자식 블록에서의 내용을 원하는 대로 합쳐서 사용할 수가 있다.
Jinja 의 상속 기능은 편리하나 주의해야 할 부분은 전부를 가져오거나 안 가져오거나라는 두 가지 선택밖에 없다는 점이다. 그렇기 때문에 템플릿을 설계하고 작성할 때에 주의해야 하며, 최대한 상속의 횟수는 줄이는 것이 좋다. 한 부분을 수정하기 위해 부모, 자식을 모두 수정해야되는 경우가 생길 수 있다.
부트스트랩은 프론트엔드에 대한 최소한의 지식으로 다양하고 예쁘게 꾸밀 수 있도록 도와주는 도구이다. 단순한 웹 애플리케이션을 제작하고, 프론트엔드에 큰 비중이 없을 때는 최소한의 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>
다양한 컴포넌트들은 부트스트랩 문서에서 확인하고 복사해서 사용할 수 있다.
🎄 참고문서
Flask 어플리케이션 작성 및 실행, 환경변수 설정
Flask란?
순환참조 원인과 해결방법
파이썬 라이브러리, 모듈, 패키지...
Flask 공식문서
🎨 회고
오늘 체력도 안좋고 상태도 안좋아서 다 포기하고 싶었는데 어떻게 꾸역꾸역 글까지 정리했다. 잘했다 잘했어ㅠㅠㅠ 강의만 들을 때는 이해가 안됐는데 정리하면서 실습해보니까 어느정도 이해가 된다. 근데 아직 개념이 확실하게 자리잡진 않았다. 어쨌든 오늘거는 파이썬을 가지고 가볍게 웹프로그래밍을 하는걸 배운듯? 웹은 더 알아보고 싶은 마음이 든다. 프론트엔드는 어느정도 해본적도 있고 하니까 더 관심이 가는편이다. 끝나면 이부분은 더 공부해봐야겠다.