Flask, Jinja, Bootstrap, Template Engine, Web Application
Micro Web Framework
이며, 파이썬을 활용하여 웹 어플리케이션을 작성할 수 있도록 도와준다.Jinja
를 비롯해 Werkzeug
, Flask-SQLAlchemy
등의 패키지와 라이브러리 등이 있어 개발이 수월하도록 도와준다. Flask를 시작하는 기본 순서는 다음과 같다.
$ pip install flask
mkdir
명령어를 사용하여 Flask 어플리케이션을 저장할 폴더를 생성하는데, 폴더 이름은 주로 어플리케이션 이름으로 한다고 한다.__init__.py
파일 만들기.__init__.py
파일에 들어갈 기본 내용은 다음과 같다.# __init__.py
from flask import Flask
app = Flask(__name__)
# __init__.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World!'
test.com/user/...
이런 식으로 붙는게 있는데 이렇게 앞 꼬리를 따라서 쭉 이어지는 거라고 생각하면 된다! (/로 이어진 걸 Endpoint
라고도 한다. 여러 량이 이어진 기차를 생각하면 쉬울 것 같다!)@app.route('/', methods=['POST', 'GET'])
def index():
...
GET, HEAD, OPTIONS
는 디폴트로 제공하고, 그 외 POST, PUT, PATCH, DELETE
등을 쓰려면 추가를 해줘야 한다. @app.route('/', methods=['GET'])
이렇게 해두면 GET만 쓸 수 있다는 뜻이다! 다른 head, options를 쓸 수 있는 게 아냐~@app.route('/index/<num>')
def index_number(num):
return 'Welcome to Index %i' % int(num)
127.0.0.1:5000/index/num
를 치면 그 변수에 맞는 값이 웹 페이지에 리턴되어 보인다는 얘기지~ (플라스크에서 변수는 <>
를 사용함!)블루프린트 기능
을 통해 많아지는 라우터를 한 곳에 묶어둘 수 있는데, 이건 내가 오늘 보긴 했지만 직접 써보진 않아서 다음에 공부노트로 해보기로 하자ㅠㅠ circular import
를 피하기 위해 이 사용을 추천하고 있다고 한다.circular import (순환 참조)
- A가 B를 참조하고, B가 A를 참조하고. CLI 기본 명령어
$ FLASK_APP=app_name flask run
웹 템플릿 엔진
이다. 즉, 템플릿 엔진은 맞춤형 웹 페이지를 자동으로 생산할 수 있도록 도와준다render_template
함수를 통해 index.html
에 변수 전달하기! index.html
에 다음과 같은 코드가 있다고 해보자.(... 생략 ...)
<body>
<h2>Apple is {{ fruit_color }}</h2>
<h2>{{ number }} 개의 과일이 있습니다.</h2>
</body>
(... 생략 ...)
__init__.py
에서 변수로 넘겨준 부분을 html 내에서 받아 웹 페이지에 보여주는 거라고 생각하면 된다. 이렇게 넘길 수 있다.(... 생략 ...)
@app.route('/')
def index():
apple = 'red'
apple_count = 10
return render_template('index.html', fruit_color=apple, number=apple_count)
apple
, apple_count
를 픽스해서 넘겨줬지만, 상황에 따라 가변적으로 바꿀 수도 있겠지! 이런 느낌으로 생각하면 된다.객체 태그
, if
for
등 자주 사용되는 기능들이 있는데 오늘 다 써보지는 못해서 필요시 공식문서의 여기를 참고하자.HTML
을 빠르고 예쁘게 꾸밀 수 있는 도구이다.오늘은 아래 실습을 해보았다. 하면서 궁금한 건, 메모할 것은 주석으로 달아두었다.
- index 함수에서는 '/' 엔드 포인트로 접속했을 때 'templates' 폴더 안에 있는 'index.html' 파일을 렌더링 해줍니다.
- 요구사항:
- HTTP Method: GET
- Endpoint: /- 상황별 요구사항:
- GET 요청이 들어오면 templates/index.html 파일을 렌더해야 합니다.
- users 함수는 'users.csv' 파일을 읽은 뒤에 파일에 있는 유저들을 리스트에 담아 다음과 같은 형태로 응답해야 합니다.
- CSV 파일을 읽어온 뒤에 가장 첫 줄인 'username' 은 리스트에 담지 않습니다!
- 요구사항:
- HTTP Method: GET
- Endpoint: /users
- display_user 함수에서는 'users.csv' 파일의 유저 중에서 전달받은 { 유저 아이디 } 번째 유저를 문자열로 돌려줘야 합니다.
(만약에 사용자가 { 유저 아이디 } 를 명시하지 않은 경우에는 유저 아이디는 '1' 이라고 가정합니다.)
- 요구사항:
- HTTP Method: GET
- Endpoint: /users/{ 유저 아이디 } (유저 아이디는 1 이상의 숫자입니다)- 상황별 요구사항:
- 문제 주석 참고
[1번]
"""
[Memo]
- flask run - '$ FLASK_APP=simple_flask_app/app.py flask run' => 뒤에 경로 안 붙이면 안 됨. __init__.py 이름이 아니라 그런가?
- 디버깅 모드 실행
$ export FLASK_APP=simple_flask_app/app.py
$ export FLASK_ENV=development
$ flask run
"""
"""
Simple Flask App 입니다.
간단한 플라스크 애플리케이션을 만들어 보면서
어떻게 동작을 하는지 살펴보세요!
"""
from crypt import methods
from email.policy import default
import os
from flask import Flask, render_template # render_template 추가함
import csv, json # 추가함
# 추가로 필요한 패키지들이 있는 경우 가져와 사용합니다.
# 'app' 은 플라스크 애플리케이션 객체입니다.
app = Flask(__name__)
# CSV 파일이 어디에 있는지 알려주는 설정입니다.
# CSV 파일은 수정하지 않습니다!
app.config['USERS_CSV_FILE'] = os.path.join(os.path.dirname(__file__), 'users.csv')
# 플라스크 애플리케이션의 라우팅 설정입니다.
# 서버 주소 + '/' 로 들어가면 아래 함수가 실행이 됩니다.
@app.route('/', methods = ['GET'])
def index():
"""
index 함수에서는 'simple_flask_app/templates' 폴더에 있는 'index.html'
파일을 렌더링 해줘야 합니다!
"""
return render_template('index.html')
[2번]
@app.route('/users', methods = ['GET'])
def users():
"""
users 함수에서는 사용자가 '서버 주소 + /users' 로 접속하게 되면
'users.csv' 파일을 읽은 뒤에 파일에 있는 유저들을 리스트에 담아 다음과 같이 dictionary 형태로 넘겨줘야합니다.
{
"users": ["spongebob", "patrick", "squidward"]
}
NOTE: CSV 파일을 읽어온 뒤에 가장 첫 줄인 'username' 은 리스트에 담지 않습니다! """
# 빈 리스트, 딕셔너리 생성
users_1 = []
users_2 = {}
# csv 파일 불러오기
with open(app.config['USERS_CSV_FILE'], 'r') as csvfile:
names = csv.reader(csvfile)
for name in names:
users_1.append(name[0]) # 빈 리스트에 이름만 넣어두기 (cf. 리스트로 뽑혀서 슬라이싱 해서 값만 넣어주는 것)
# username 제외 딕셔너리에 할당.
users_2['users'] = users_1[1:]
return users_2
"""
궁금증
# 난 출력하면 '{"users":["spongebob","patrick","squidward"]}' 한 줄로 나오는데 상관 없나? 일단 pytest 통과하긴 하는데..
# json으로 바꿔주고 넘겨도 출력되는 형태가 똑같긴 함.
users_2 = json.dumps(users_2)
"""
[3번]
@app.route('/users/', defaults = {'user_order': 1})
@app.route('/users/<user_order>', methods = ['GET'])
def display_user(user_order):
"""
display_user 함수에서는 사용자가 '서버 주소 + /users/{ 유저 아이디 }' 로
접속하게 되면 'users.csv' 파일의 { 유저 아이디 } 번째 유저를 문자열로
돌려줘야 합니다.
만약에 사용자가 { 유저 아이디 } 를 명시하지 않은 경우에는 유저 아이디는 '1'
이라고 가정합니다.
예시)
('users.csv' 파일에 'spongebob', 'patrick', 'squidward' 순으로 적혀있다고
가정합니다.)
- 예를 들어 사용자가 '/users/' 에 접속하면 { 유저 아이디 } 가 주어지지 않았기
때문에 '1' 번째, 즉 첫 번째 유저인 'spongebob' 문자열을 돌려줍니다.
- 예를 들어 사용자가 '/users/1' 에 접속하면 '1' 번째, 즉 첫 번째 유저인
'spongebob' 문자열을 돌려줍니다.
- 예를 들어 사용자가 '/users/2' 에 접속하면 '2' 번째, 즉 두 번째 유저인
'patrick' 문자열을 돌려줍니다.
"""
#------ users() 함수 내용 시작 ------#
# 빈 리스트, 딕셔너리 생성
users_1 = []
users_2 = {}
# csv 파일 불러오기
with open(app.config['USERS_CSV_FILE'], 'r') as csvfile:
names = csv.reader(csvfile)
for name in names:
users_1.append(name[0]) # 빈 리스트에 이름만 넣어두기 (cf. 리스트로 뽑혀서 슬라이싱 해서 값만 넣어주는 것. 이렇게 안 하고 extend() 해도 됨)
# username 제외 딕셔너리에 할당.
users_2['users'] = users_1[1:]
#------ users() 함수 내용 끝 ------#
# 이름 슬라이싱
res = users_2['users'][int(user_order) - 1]
return res, 200
"""
[기록] - 이 문제를 풀면서 의아했던 것
1. 문제의 '{ 유저 아이디 }'를 무조건 지켜야만 하는 줄 알았다.
- 그래서 아니 flask 변수 표현은 '<>'로 하는데 이건 뭐지? 싶어 상당히 헤맸다.
2. 라우트 설정시 '/users/<int:user_order>' 로 이 단계에서 int 지정해주면 왠지 모르지만 Pytest에서 틀렸다고 나온다.
- 아래서 지정하나 위에서 해주나 결과는 똑같은데!! 내가 모르는 뭔가가 있는 건가?
- 진짜 혹시 몰라서 위에서 안하고 아래에 직접 해줬더니 통과했다. 침착하자...
"""
# 문제 주어질 때 기본으로 있던 코드
if __name__ == "__main__":
app.run(debug=True)
users()
함수를 그대로 가져올 수도 있었지만 그냥 해당 함수의 코드를 그대로 3번에도 옮겨왔다. 함수 여러 개 있을 때 다른 함수의 값을 참조하면 상황에 따라 안될 수도 있을 것 같아서이다. (직접 쓸 함수 뿐 아니라, 그 함수가 참조한 함수도 import 안 하면 오류가 날 것 아닌가?)github.io
같은 것이 그런 정적 블로그들이라고 한다. IP
에 대한 추가 설명 (출처)ICANN
이라는 국제기관에서 IP를 관리하고, 국가별 IP 분배 등을 담당한다.IPv6
!!오, 이게 정녕 하루만에 칠 수 있는 공부량이란 말입니까~
였다. 실제로 오늘 배운 내용 중 실습까지 해보지 못한 부분이 지금까지와 비교해도 많지 않았나 싶다.