Flask-Dash-Heroku 연동
Page content
개요
- Flask 및 Dash를 활용하여 간단한 대시보드를 생성할 수 있다.
- 기존 구현한 대시보드를 Heroku에 배포할 수 있다.
사전준비
- 파이썬 가상환경 설치 및 기존 라이브러리에 대한 이해가 어느정도 있음을 가정한 상태에서 본 블로그를 작성했음을 유의한다.
- Heroku 회원가입 및 로그인이 되어 있어야 한다.
Step 1. Github Repo생성
- Github Repo 생성 시, 중복되지 않을 법한 이름으로 생성
- 필자 Repo : flask-heroku-dash-evan1234
- 해당 Repo를 로컬로 가져온다.
git clone https://github.com/your_name/your_unique_repo.git
Step 2. 가상환경 설치 및 주요 라이브러리 설치
- 먼저 가상환경을 설치한다.
virtualenv venv
- 가상환경에 접속한다.
source venv/Scripts/activate
- 주요 라이브러리를 설치한다.
- pandas : 데이터 불러오기 및 가공
- dash & plotly : 동적 시각화 대시보드 제공 라이브러리
- Flask : Flask 웹 프레임워크
- SQLAlchemy : 데이터베이스 연동 프레임워크
pip install dash plotly Flask pandas gunicorn psycopg2-binary SQLAlchemy Flask-SQLAlchemy
Step 3. 기본 배포 테스트
- 먼저 본격적인 코드에 앞서 기본적으로 배포가 되는지 확인한다.
- 필요한 파일
app.py
,Procfile
,runtime.txt
,requirements.txt
파일이 필요하다.
(1) app.py
- 아래와 같이 작성한다.
# -*- coding:utf-8 -*-
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return "Hello World"
(2) Procfile 파일 작성
- 대문자 및 소문자 정확하게 기재해야 한다.
web: gunicorn app:app
(3) runtime.txt 파일 작성
python-3.9.12
(4) requirements.txt 파일 작성
- 해당 파일은 기존에 설치했던 라이브러리를 모두 호출하는 형식이어야 한다.
- 프로젝트
Root
경로에서 아래와 같이 실행한다.
pip freeze > requirements.txt
- 전체 파일 구조를 확인하면 아래와 같다.
$ ls
app.py Procfile README.md requirements.txt runtime.txt venv/
(5) wsgi.py 파일 작성
- 먼저 wsgi는 일종의 프로토콜인데, Python Application이 웹 서버와 통신하기 위한 표준 인터페이스를 제공한다.
- 자세한 내용은 관련 문서를 참조한다.
- 코드는 다음과 같이 작성한다.
from app import app
if __name__ == "__main__":
app.run(threaded=True, port=5000)
(5) 배포 시작
- 아래 명령어를 순차적으로 입력하여 실행한다.
- heroku login 시, Web UI에서 실제 ID와 Password를 입력해야 한다.
- heroku create github repo와 동일하게 작성한다.
heroku login
heroku create your_project_repo
git add .
git commit -m "initial updated"
git push # github repo에 추가
git push heroku main
(6) 배포 사이트 확인
- 실제로 정상적으로 배포가 완료가 되었다면 실제 웹사이트 URL을 클릭 후, 아래와 같이 확인할 수 있어야 한다.
- 배포가 진행이 안된다면, 그 다음 코드를 입력하는 것은 의미가 없기 때문에 확인 후 넘어가도록 한다.
Step 4. Flask & Dash
- Flask와 Dash를 연동하는 메인 코드는 다음처럼 작성해야 한다.
- 기본적으로 1개의 Flask 서버에 다수의 Dash App을 연결하는 방식을 사용한다.
- 주의) 이 방식이 좋은 방식은 아니다. 추천 방식은 Flask Application Factory를 추천한다.
- Flask 서버인 app을 생성하고 Dash에서 처리해야 하는 URL은
url_base_pathname
으로 전달한다.
from flask import Flask
from dash import Dash
app = Flask(__name__)
dash_app1 = Dash(__name__, server = app, url_base_pathname='/dashapp1/')
.
.
.
@app.route("/")
def index():
return "Hello World!"
- 이제 Flask & Dash가 정상적으로 연동하는지 선택하기 위해 아래와 같이 기존 app.py를 수정한다.
- dash layout를 작성하는 구체적인 방법은 문서를 참조한다.
from flask import Flask
from dash import Dash, dcc, Input, Output
from dash import html
import plotly.express as px
app = Flask(__name__)
dash_app1 = Dash(__name__, server = app, url_base_pathname='/dashapp1/')
# -- dash_app1
dash_app1.layout = html.Div([
html.H1('Hello Dash'),
])
@app.route("/")
def index():
return "Hello World!"
- 배포를 하면 각 본인의 URL에 /dashapp1/ 을 추가한다.
Step 5. 소스코드 업데이트
html.Div([])
코드에 계속 추가를 할 것이다.- 이 때, 확인해야 하는 주요 튜토리얼은
Dash Core Components
이다. - 추가된 코드는 크게 두가지이다.
dcc.Slider
&dcc.Graph
이다.
dcc.Graph
코드는 크게 figure 객체를 생성하며, data & layout 영역으로 구분된다.
from flask import Flask
from dash import Dash, dcc, Input, Output
from dash import html
import plotly.express as px
app = Flask(__name__)
dash_app1 = Dash(__name__, server = app, url_base_pathname='/dashapp1/')
# -- dash_app1
dash_app1.layout = html.Div([
html.H1('Hello Dash'),
dcc.Slider(0, 9, marks={i: f'Label{i}' for i in range(10)}, value=5),
dcc.Graph(
figure=dict(
data=[
dict(
x=[1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012],
y=[219, 146, 112, 127, 124, 180, 236, 207, 236, 263,
350, 430, 474, 526, 488, 537, 500, 439],
name='Rest of world',
marker=dict(
color='rgb(55, 83, 109)'
)
),
dict(
x=[1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012],
y=[16, 13, 10, 11, 28, 37, 43, 55, 56, 88, 105, 156, 270,
299, 340, 403, 549, 499],
name='China',
marker=dict(
color='rgb(26, 118, 255)'
)
)
],
layout=dict(
title='US Export of Plastic Scrap',
showlegend=True,
legend=dict(
x=0,
y=1.0
),
margin=dict(l=40, r=0, t=40, b=30)
)
),
style={'height': 300},
id='my-graph-example'
)
])
@app.route("/")
def index():
return "Hello World!"
- 다시 배포를 해본다.
Step 6. 다중 페이지 생성
- 이제 다중 페이지를 작성해본다.
- 기존과 동일한 작업을 한다.
- 이번에 추가적으로 확인해야 하는 코드는 callback 이다.
from flask import Flask
from dash import Dash, dcc, Input, Output
from dash import html
import plotly.express as px
app = Flask(__name__)
dash_app1 = Dash(__name__, server = app, url_base_pathname='/dashapp1/')
dash_app2 = Dash(__name__, server = app, url_base_pathname='/dashapp2/')
# -- dash_app1
dash_app1.layout = html.Div([
html.H1('Hello Dash'),
dcc.Slider(0, 9, marks={i: f'Label{i}' for i in range(10)}, value=5),
dcc.Graph(
figure=dict(
data=[
dict(
x=[1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012],
y=[219, 146, 112, 127, 124, 180, 236, 207, 236, 263,
350, 430, 474, 526, 488, 537, 500, 439],
name='Rest of world',
marker=dict(
color='rgb(55, 83, 109)'
)
),
dict(
x=[1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012],
y=[16, 13, 10, 11, 28, 37, 43, 55, 56, 88, 105, 156, 270,
299, 340, 403, 549, 499],
name='China',
marker=dict(
color='rgb(26, 118, 255)'
)
)
],
layout=dict(
title='US Export of Plastic Scrap',
showlegend=True,
legend=dict(
x=0,
y=1.0
),
margin=dict(l=40, r=0, t=40, b=30)
)
),
style={'height': 300},
id='my-graph-example'
)
])
# dash app 5 width pandas
df = px.data.stocks()
dash_app2.layout = html.Div([
html.Br(),
html.H2('Time series graph with Dash'),
html.Br(),
dcc.Dropdown(
id="ticker",
options=[{"label": x, "value": x} for x in df.columns[1:]],
value=df.columns[1],
clearable=False,
),
dcc.Graph(id="time-series-chart"),
])
@dash_app2.callback(Output("time-series-chart", "figure"), [Input("ticker", "value")])
def display_time_series(ticker):
fig = px.line(df, x='date', y=ticker)
return fig
@app.route("/")
def index():
return "Hello World!"
- 배포 후 확인해본다.