블로그 이미지
자유로운설탕

calendar

1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

Notice

2017. 7. 20. 15:44 프로그래밍

  이번 시간에는 파이썬 웹 프레임워크로 많이 쓰이는 경량화 웹 프레임워크라고 불리우는 플라스크(flask) 를 살펴보는 시간을 가지려고 한다. 메뉴얼을 기반으로 전체적인 플라스크의 구조에 대해서 살펴보고, 지난 legacy web 시간에 구현했었던 DB의 테이블을 조회해 HTML 테이블로 표현하는 예제, Javascript 계의 matplotlib 이라 할수 있는 D3.js 와 결합하여 json 데이터를 가져다가 그래프를 그려주는 예제, 마지막으로 matplotlib 을 이용해서 파이썬 코드 기반의 그래프를 생성하여 HTML 문서에 포함 시키는 총 예제 3가지를 소개하려고 한다. 

 

 

 

[목차]

0. 왜 파이썬 공부에 구글을 이용하는게 좋은가?

1. 언어를 바라보는 방법. 파이썬을 어떻게 바라봐야 할까?

2. 파이썬 설치와 환경, 버전 선택 하기의 이유.

3. 만들고자 하는 기능을 모르는 조각으로 나눠 조사해 보기

4. 데이터 베이스에서 내용 가져와 출력하기

5. 암호화 모듈을 이용해 암복호화 해보기

6. 퍼즐 조각들을 합쳐보기

7. 엑셀 파일 사용해 보기 -> 부록 : fuction 을 이용해서, 코드 정리해 보기

8. 정규표현식을 왜 사용해야 할까? 언어속의 미니 언어 정규표현식 살펴보기

9. 입력과 결과를 GUI 화면과 연결해 보기

10. Whois API 이용해 보기

11. 웹페이지 호출해 내용 파싱 하기(BeautifulSoup 그리고 한계)

12. 자동화 - 웹 자동화(with Selenium)

13. 자동화 - 윈도우즈 GUI 자동화(with pywinauto)

14. 자동화 - 작업 자동화

15. 수학 라이브러리 살펴보기

16. 그래픽 라이브러리 살펴보기

17. 머신러닝에서의 파이썬의 역활

18. 웹 프로그래밍 - Legacy Web

19. 웹 프로그래밍 - Flask 살펴보기(feat. d3.js)

20. 웹 프로그래밍 - Django 살펴보기

21. 정리 - 이런저런 이야기

 

 

 

 

[들어가면서]

  우선 시작하기 전에 얘기하고 싶은 것은, 프레임워크란 부분에 너무 많은 기대는 하지는 말라고 하고 싶다. 프레임워크란 해당 분야의 좋은 관행(best practice)들과, 지원용 라이브러리들을 모아 놓은 범용적인 틀 같은 것이여서 개발에 대한 방법론, 자주 쓰는 기능 라이브러리, 안전한 설계 이슈 등 많은 부분에 도움을 줄 수 있겠지만, 그 범용적인 부분이 현재 자기가 만들고자 하는 특정한 프로그램에 적합 하다는 보장은 못해주며, 자신이 만드려는 프로그램에 맞게 커스터마이즈 하기 위해서는 많은 노력이 추가로 들어가게 될 것 이다. 마치 집을 짓는데, 기본 재료와 반듯한 땅, 숙련된 기술공을 제공해 준다 해도, 해당 지원 부분이 내가 원하는 집을 짓는데 필요한 디테일의 전부는 절대 못 된다는 것이다. 해당 프레임워크가 지원하는 언어를 충분한 깊이로 이해하고, 해당 분야(웹, 디비, 시스템, 빅데이터 등등)에 대해 충분히 이해한 상태에서 사용해야, 사용하려는 프레임워크에 대한 이해도도 깊어지고, 적절하게 스스로나 해당 프레임워크가 의도한 대로 사용이 가능할 것이다. 추가로 비슷하거나 상이한 다른 프레임워크들에 대해서도 핵심을 잘 이해하고 있다면, 선택한 프레임워크의 장, 단점에 대해서 객관적으로 바라보고 선택에 대한 트레이드오프를 잘 따져볼 수 있을 것 같다

 

 

[한글 문서 여부]

  Flask 에 대한 메뉴얼 문서는 구글을 찾다보면 한글문서가 있긴 한데, 현재 0.12 버전이 나온상태에서, 파이썬 3 지원이 안됬던 0.11 개발기간 당시의 버전 문서의 번역본이고, 번역이 완전히 다 되진 않은 상태라서, 처음 접할때 전체적인 맥락에 대한 살펴보기 용으로만 쓰라고 권하고 싶다. 또 구글에서 검색하다보면 나오는 간단한 예제들을 구현해 놓은 여러 한글 블로그들도 많으니, 본격적으로 영문 문서를 보기 전에 미리 사전 지식을 쌓아놓으면 좀 더 읽기 수월해 질 듯 하다.

http://flask-docs-kr.readthedocs.io/ko/latest/index.html

 

 

[Flask vs Django]

  그럼 우선 파이썬에서 보통 많이 얘기되는 두 개의 웹 프레임워크를 한번 비교해 보자. 구글에서 'flask vs django' 라고 넣고 검색해 보면 아래와 같은 여러 비교한 페이지들이 나온다.

 

[플라스크와 장고 비교 글 - 이 사람은 플라스크를 더 편하다고 생각한다.] 

https://www.codementor.io/garethdwyer/flask-vs-django-why-flask-might-be-better-4xs7mdf8v

[플라스크, 장고, 피라미드 비교 - 밑에 것은 번역 글]

https://www.airpair.com/python/posts/django-flask-pyramid

http://kmc5500.tistory.com/162

 

  대충 요약해 보면, flask 는 웹 프레임워크가 필요한 최소한의 기능만 제공 하고, 나머지는 외부 모듈이나, 개발자에게 구현을 하도록 유도하는 편이라고 하고, Django 는 중, 대규모의 사이트를 목적으로 만들어져 일반적인 웹사이트 개발에 필요한 풀 패키지를 지원하는 편이라고 한다. 글 들에서도 나오지만 판단은 각자 스스로 하는게 맞을 것 같고, 이번 시간에는 flask 가 표방하는 최소한의 웹 프레임워크가 어떤 의미인지 살펴보도록 하겠다.

 

 

[Flask Documentaton 보기]

  구글에 'flask documentation' 이라고 검색하면 아래의 최신 0.12 버전의 프레임워크에 대한 설명이 있는 링크를 얻을 수 있다.

http://flask.pocoo.org/docs/0.12/

 

  예전 수학 라이브러리 볼때 처럼, 쭉 목차를 살펴 보면, 먼저 'User Guide' 에는 Installation(설치) 부분이 있고(개인적으로 여러 버전을 운영할 일은 없을 듯 해서 vitualenv는 고려하지 않았다),  Quick Start 섹션에 기본적으로 기본적인 flask 웹 기동이나, url 을 파싱해 해당되는 파이썬 함수에 전달하는 라우팅, css 나 img 같은 정적인 파일에 접근하는 방법, 템플릿을 꾸며서 원하는 웹 화면을 보여주는 방법, request 된 데이터를 받아 처리하는 방법 등이 설명되어 있다. 그리고 Tutorial 에는 세팅 방법과, DB 를 조회해 화면에 보여주는 예제가 있는 것 같고, Templates 에는 여러가지 템플릿 제어 방법에 대한 설명, 한참 아래의 Patterns for Flask 에는 여러가지 플라스크로 웹을 구성하는 권장 기법들이 있다. 그 밑의 'API Reference''Additional Notes' 는 위의 사용자 가이드가 익숙해 졌을 때 추가적인 정보를 보기 위해 살펴보는게 맞을 거 같다(개인적으로 flask 를 둘러보면서 예제를 구현해 보려고 했을때 진행했던 흐름이다).

 

 

[Routing]

  실용적으로 보이는 프레임워크긴 하지만, 실제 웹코드 구현에 들어가기 전에 legacy 하고 차이가 나는 부분에 대해서 설명 후에 들어가려 한다(혹시 웹 프로그래밍에 대해서 익숙하지 않은 상태라면 앞 교시인 Legacy Web 파트를 꼭 읽어 보시고 오길 권한다). 먼저 routing 와 static files 에 대해서 생각해보자. 현재 가상현실, 증강현실이 주목받는 세상이지만, 컴퓨터 자체가 어느 정도는 해당 개념들의 표본이 아닌가 싶다. 컴퓨터 내의 파일이라는 개념에 대해 우리는 동영상, 그림파일, 문서파일, 음악파일 등을 자연스럽게 사용해와서 실체화된 것이라고 생각하지만, 실제로는 메모리나 디스크 상에 구분된 숫자에 불과하다는 사실은 맞을 것이다. 그 정보를 운영체제가 해석을 해서, 폴더나 파일로 구분하여 인식하고, 응용 프로그램이 해당 데이터를 전달 받아 우리가 볼 수 있도록 화면에 출력하거나, 소리로 출력하여 노래를 듣거나, 문서를 보거나 하는 것일 것이다. 일례로 오피스가 설치되 있지 않은 컴퓨터라면 doc, xls 파일은 아무 의미가 없는 파일일 것이다. 예컨데 특정한 처리를 해주는 로직을 만나야지만 우리가 파일이라고 믿는 것들이 의미가 생긴다는 것이다.

 

  비슷하게 우리가 legacy web 에서 'test.asp', 'hey.php' 같은 파일들이, 웹 프로그램 확장자를 가진 파일이여서, IIS, Apache 같은 웹 서버에서 해당 파일이 실행이 된다고 믿어왔던 것도 어떤 측면에서는 관념적인 것에 불과할지도 모른다. 역으로 얘기하면 어떤 이름과 확장자를 가진 URL 의 호출이 웹서버에 주어질 때, 그것을 어떻게 해석하냐는 폴더와 파일이라는 물리적인 요소에 달려있는게 아니라, 웹 서버가 요청을 어떻게 해석을 하냐는 논리적인 요소에 달려있다고 볼수 있다 

 

  이 부분이 최근의 웹 서버 모듈에서 볼수 있는 'routing' 이라는 개념이다. 클라이언트가 url 주소에 hey.php 를 요청하든 hey 를 요청하든, 웹 서버 모듈만 지원을 한다면, 해당 파일이라는 형태에 국한되지 않고 해석을 해,  파일 경로를 찾은 후 처리하는 대신에 특정한 로직(함수)으로 직접 전달할 수 있다. 즉 파일과 디렉토리 기반으로 움직이던 웹 서버의 동작을, URL 경로 규칙에 기반한 함수와의 연결 로직으로 추상화(혹은 일반화) 시켰다고 봐도 될 듯 싶다(그래서 개인적으로 확장자를 가진 웹에만 익숙하다가, 어느날 확장자가 없는 처음 웹을 만났을 때 웹서버 내에서 url 에 해당하는 실제 파일의 경로를 찾을 수가 없어서 당황했었던 기억이 난다). 결국 이렇게 되면 기존 웹에서 의미가 있었던 디렉토리와 파일명, 확장자는 모두 의미가 없는 껍데기가 된다. 이러한 URL 과 내부 기능과의 직접적인 연결을 하는 방식을, 네트워크에서 패킷을 적절한 경로로 안내하는 라우터의 역활을 차용해서 routing 이라고 명명한 것 같다.

 

 

[Static Files]

  같은 맥락에서 보면 이제 프로그램 확장자 파일이 아닌 css, jpeg 같은 정적인 파일에 대해서도 기존 웹과 같이 url 기반의 디렉토리와 파일이름 경로로 접근하기는 좀 힘들어 지게 됬다. 왜냐면 이젠 웹서버 모듈은 기존 웹과 같이 웹루트 폴더 기준의 트리 구조로 된게 아니라, 기본으로 라우팅이 되는 논리적 레벨의 매핑이기 때문이다. 그러한 논리적 레벨의 매핑을 특정 디렉토리를 기반으로한 물리적(이것도 앞에서 얘기했듯이 넓게보면 가상이긴 하지만) 매핑으로 잠시 변환하는 기능이, static files 이라고 보면 될것 같다. (flask 에서는 dynamic web, static web 이라는 표현으로 설명한다)

 

 

[Rendering Templates]

  예제를 구현하기 필요한 마지막 개념은 Rendering Templates 섹션이다. 사실 template 란건 특정 UI 의 재사용을 위한 개념으로 많이 사용되기는 하는데, flask 에서는 꼭 재사용이 아니더라도 UI 를 표현하기 위해선 하나의 템플릿(UI 를 표현한다는 측면과 재사용 적인 측면의 의미를 동시에 가졌다고 볼 수 있을 듯 싶다)을 사용해야 된다고 생각하면 된다. dynamic web 인 flask 의 어플리케이션 모듈 쪽에서 표현에 사용할 데이터를 준비한 후, 해당 데이터를 지정한 템플릿에 연관해서 처리를 한다. 이러한 구조가 HTML 과 어플리케이션 로직을 분리하기 위해서 라고 볼 수도 있겠지만, 사실 웹 자체의 베이스가 동적이여서 페이지 개념이 없어졌기 때문에, 최종적으로 페이지를 동적 프로그램 코드내에서 생성하게 되면, 이번엔 반대로 프로그램 코드에 UI코드가 섞이게 되어 의미가 없게 되니, 어쩔수 없이 최종으로 UI 를 표시하는 부분을 템플릿이라는 개념으로 다시 떼어낸 것도 같다. 마치 Javascript 와 HTML 을 event 속성이 연결하였듯이, 템플릿 또한 순수한 html 코드는 아니고, 앞의 ASP 의 <% %> 코드와 비슷하게, 전달된 데이터들을 템플릿 사이에 적절히 끼워주는 방식으로 구현된다(해당 작업을 렌더링이라고 표현하는 듯 하다).

 

 

[사전 준비 - Flask 설치]

파이썬 3를 지원하므로 pip 명령어로 설치하면 된다.

c:\Python\flaskweb>pip install flask

Collecting flask
....
Installing collected packages: flask
Successfully installed flask-0.12.2

 

 

  c:\python 폴더에 flaskweb 폴더(이 폴더 이름은 다른 아무 이름이나 된다)를 만든다. 다시 flaskweb 폴더내에 templates 폴더(이 폴더 이름은 약속된 이름이기 때문에 꼭 이 이름으로 만들어야 된다)를 만든다.

 

 

 

 

[DB 에서 데이터 불러와 HTML 테이블로 보여주기]

  첫번째 예제로 4교시에서 만들었던 예제를 응용해, MSSQL Server 에서 데이터를 불러 HTML 테이블로 출력하는 예제를 보이려고 한다. 여기에서 기본적인 routing, template 를 다루는 코드가 나오니 예제로 개념을 익히면 된다. 18교시와 마찬가지로 4교시에 만든 supermarket 테이블과 python 코드를 그대로 재사용 하려고 한다(4교시를 안해보셔서 환경이 없는 분들은 MSSQL Server 설치와 테이블 생성을 하시고 오셔야 한다).

 

 

[App Code 구현]

  해당 방식 구현을 위해 구글에서 'flask db to html table' 이라고 검색해서, 아래 3개의 페이지를 얻었다.

[DB 데이터를 템플릿에 넘기는 방식을 볼수 있음 - 템플릿 출력 부분 코드가 명확히 표현되 있진 않다.] 

https://stackoverflow.com/questions/29525758/data-from-sqlite-to-an-html-table-in-a-flask-page

[템플릿 출력 하는 코드가 명확히 나옴]
https://stackoverflow.com/questions/42040379/creating-an-html-table-with-database-values-in-flask

[메인 페이지에 SQL 초기화와 조회 관련 코드를 어떻게 배치할까에 대한 힌트]
https://stackoverflow.com/questions/38540256/flask-python-mysql-how-to-pass-selected-data-though-a-for-loop-and-return-it

 

 

  앞의 사용자 메뉴얼의 튜토리얼을 읽어 전체적인 분위기를 파악 후, 아래 3개의 코드들을 참조하고, 4교시에 만들어 놓았던 파이썬 코드를 결합시킨 최종 코드는 아래와 같다. 간단히 설명하면, flask 는 웹서버와 웹어플리케이션 기능을 같이 실행하는데(운영 단계에선 아파치 등과 연계하는게 맞을 듯은 싶다), 127.0.0.1 의 포트 5000번으로 서비스가 된다. 'sqltable' 경로가 호출되어 'showsql()' 함수가 시작이 되면, 지정된 SQL 문을 실행하여, templates 폴더에 있는(flask 의 몇 안되는 미리 약속되 있는 폴더이다) 'myweb.html' 파일과 DB 에서 가져온 전체 결과값(fetchall)을 지닌 'rows' 변수를 (아마 list)를 이용해서 렌더링 하여(rendering) 결과를 표시한다. 아래 코드는 결과를 표현해주는 템플릿 코드가 아직 만들어지지 않았기 때문에 아직은 반쪽의 코드라고 볼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from flask import Flask
from flask import render_template
import pymssql
 
# MSSQL 연결 하기 
conn = pymssql.connect(server='localhost', user='pyuser', password='test1234', database='mytest')
cursor = conn.cursor()
 
# flask web 실행
app = Flask(__name__)
 
# 'sqltable' 이라는 URL 인자를 'showsql' 이라는 함수로 연결한다. 
@app.route("/sqltable")
def showsql():
    # SQL 문을 실행하여 supermarket 테이블에서 데이터를 가져온다.
    cursor.execute('SELECT Itemno, Category, FoodName, Company, Price FROM supermarket(nolock);')
    # 가져온 모든 데이터를 mytable.html 파일과 함께 랜더링 하여 표현한다.
    return render_template('myweb.html', rows = cursor.fetchall())
 
# 이 웹서버는 127.0.0.1 주소를 가지면 포트 5000번에 동작하며, 에러를 자세히 표시한다. 
if __name__ == "__main__":
    app.run(host='127.0.0.1',port=5000,debug=True)
cs

 

  위의 코드를 메모장에 넣고, 파일형식을 '모든파일', 인코딩을 'utf-8' 로 선택하여, c:\python\flaskweb 폴더에 myweb.py 로 저장한다.

 

 

[Templates Code 구현]

  다음은 myweb.html(이름은 호출하는 .py 파일의 이름과 달라도 무방하다) 템플릿 파일이다. 뭔가 전 시간의 ASP 코드 흐름과 비슷하지 않나 싶다. '<%' 대신 '{%' 로 파이썬 코드임을 표시하고, 안의 문법이 'vbscript' 대신 '파이썬' 문법인 차이같다.  사용자 메뉴얼을 보면 해당 템플릿을 표현하는 방식은 외부 모듈인 'Jinja2' 라는 템플릿 모듈을 차용했다라고 나온다. 대충 로직을 살펴 보면 테이블 외형을 뿌려주고('<table>태그') SQL 조회 결과를 한 줄씩 루프를 돌면서(for row in rows), '<tr>' 태그를 뿌려주고, 그 안에서 다시 해당 줄의 컬럼들을 선택하며 돌면서(for data in row), '<td>' 태그안에 그 값('{{data}}')을 넣어준다. 해당 부분은 ASP 코드랑도 비슷하고, 예전의 7교시 엑셀 시간에 배웠던 엑셀 파일로의 출력과도 비슷한 루프 구조를 가진다.

1
2
3
4
5
6
7
8
9
<table border="1" cellpadding="5" cellspacing="5">
{% for row in rows %}
    <tr>
    {% for data in row %}
        <td>{{ data }}</td>
    {% endfor %}
    </tr>
{% endfor %}
</table>
cs

 

  해당 코드를 역시 메모장에 복사하여, c:\python\flaskweb\templates 폴더에 myweb.html 로 저장한다(html 에 한글을 넣으려면 utf-8 인코딩으로 저장해야 에러가 안난다.)

 

 

  그럼 모든 코드가 구현되었고 c:\python\flaskweb\ 으로 이동하여, 아래와 같이 myweb.py 파일을 실행 한다.

c:\Python\flaskweb>python myweb.py
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 288-594-455
 * Running on
http://127.0.0.1:5000/ (Press CTRL+C to quit) 

 

  이후 브라우저를 열어 http://127.0.0.1:5000/sqltable 이라고 치면, myweb.py 상에서 sqltable 에 해당하는 showsql() 함수를 실행하여(일부러 독립된 요소라는 것을 보여주기 위해 이름들을 다 다르게 했다), 조회한 데이터를 templates 폴더에 있는 myweb.html 와 함께 랜더링 하여 아래와 같이 html 테이블을 보여준다(지금은 자연스럽게 보이지만 여기까지 올때까지의 몇 번의 시행착오 과정은 생략했다). 이 예제를 통해 기존 파이썬 로직들이 flask 라는 웹 서버 겸, 어플리케이션 모듈 프레임워크를 통해서 웹형식으로 표현되게된 흐름(flow)을 캐치하셨음 한다.

 

 

 

 

[D3.js 에서 Json 데이터 URL 를 호출해 웹으로 그래프 보여주기]

  D3.js 는 앞에서도 얘기했지만, 자바스크립트 쪽의 matplotlib 같은 시각화 라이브러리이다. 인기가 많은 편인 듯 해서, 디자인 강화에 중점을 둔 c3.js 등의 d3.js 기반의 라이브러리들도 있는 듯 하다. 해당 라이브러리의 컨셉은 목적상 matplotlib 과 역시 비슷하다(사실 모든 시각화 라이브러리가 비슷 한듯 하다). csv 등의 파일이나, 데이터를 반환하는 api 형태의 url 부터 json 데이터를 가져와서, 데이터 형을 잘 맞춰서, 원하는 그래프를 그려주는 라이브러리 함수에 공급한다. 그럼 보통 svg 형식으로 그래프를 그려(브라우저에서 HTML 문서안에 백터 그림을 나타내는 표준으로, svg와 canvas 두 가지 표준이 있다) 브라우저에 표시해 준다.

 

 

[App Code 구현]

  해당 페이지를 만들어 보기 위해 구글에서 'flask d3', 'd3 simple example', 'd3 simple date' 를 조회해서 아래의 3개의 페이지를 참고했다.

[전체적인 개념]
https://github.com/dfm/flask-d3-hello-world

[실제 동작 하는 코드]
http://bl.ocks.org/d3noob/b3ff6ae1c120eea654b5

[xxxx-xx-xx 형식의 날짜 데이터를 D3 에서 파싱하기 위해서]
https://stackoverflow.com/questions/13654609/draw-d3-simple-line-chart-with-an-array

 

 

  해당 페이지들의 예제들은 데이터 생성 로직이 조금 복잡하여, 주제에 집중하기 위해 17교시 머신러닝 예제 만들때 처럼 아래의 간단한 numpy 데이터를 임의로 만들었다.

1
2
3
   # 데이터 지정
    x = np.array(['2017-07-10''2017-07-11''2017-07-12''2017-07-13''2017-07-14'])
    y = np.array([58.1353.9867.0089.7099.00])
cs

 

 

  위의 예제들을 조합해서 정리한 프로그램쪽 코드는 아래와 같다. routing 경로가 2개로 늘어났다는 점만 제외하면, 나머진 다 앞에서 다루어 봤던 코드들이다. 조금 낯설은 코드는 json.dumps 명령어를 이용해서, json 데이터를 만들어 내는 부분이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import json
import flask
import numpy as np
 
app = flask.Flask(__name__)
 
# d3sample 을 호출했을때의 템플릿 설정
@app.route("/d3sample")
def showsample():
    return flask.render_template("d3sample.html")
 
# D3에서 가져갈 data url을 호출하면 반환할 json 데이터 만들어 내기 
@app.route("/data")
def data():
     
    # 데이터 지정
    x = np.array(['2017-07-10''2017-07-11''2017-07-12''2017-07-13''2017-07-14'])
    y = np.array([58.1353.9867.0089.7099.00])
 
    # 리스트를 json 데이터로 변환
    return json.dumps([{"date": x[i], "close": y[i]}
        for i in range(5)])
 
# 앞과 비슷한데 조금 틀려만 보임
if __name__ == "__main__":
    port = 5000
    app.debug = True
    app.run(port=port)
cs

 

  위의 코드를 메모장에 넣고, 파일형식은 '모든파일', 인코딩은 'utf-8' 로 선택하여, c:\python\flaskweb 폴더에 myweb_d3.py 로 저장한다(json.dumps 코드의 결과가 궁금 하시면 'http://127.0.0.1/data' 를 브라우저에서 호출해 본다).

 

 

[Templates Code 구현]

  그 다음은 랜더링에 사용할 d3samlpe.html 템플릿 파일이다. 안의 코드는 html 코드 보다는 d3.js 라이브러리를 사용하기 위한 자바스크립트 코드로 가득 차있다. 코드의 흐름을 보면 맨 아래 'd3.json("/data", callback)' 함수에서 'http://127.0.0.1/data' 경로를 호출하여 'json 형태의 데이터'를 얻어와서, 'callback' 함수에 넘겨준다. 'callback' 함수에서는 넘어온 데이터를 'd3.js 라이브러리' 함수에 입력하여 'svg 그래프'를 그린다. 상세한 코드들은 혹시 해당 라이브러리를 이용할 일이 있음(어차피 그래프 종류가 많아 각각 쓰임을 이해해야 한다) 이해하면 되고 여기선 flask 를 설명하는 목적이니 대충 주석과 흐름만 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
<!DOCTYPE html>
<meta charset="utf-8">
<style> <!-- 그래프 요소들의 스타일 지정 -->
body { font: 12px Arial;}
path { 
    stroke: steelblue;
    stroke-width: 2;
    fill: none;
}
.axis path,
.axis line {
    fill: none;
    stroke: grey;
    stroke-width: 1;
    shape-rendering: crispEdges;
}
 
</style>
<body>
<!-- 라이브러리 로딩. 내부에서 돌리려면 다운받아서 static 폴더에서 읽어와야 할듯 -->    
<script src="http://d3js.org/d3.v3.min.js"></script>
 
<script>
 
// 그래프 좌표 공간 설정
var margin = {top: 30, right: 20, bottom: 30, left: 50},
    width = 600 - margin.left - margin.right,
    height = 270 - margin.top - margin.bottom;
 
// 그래프 범위
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
 
// 축 정의
var xAxis = d3.svg.axis().scale(x)
    .orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
    .orient("left").ticks(5);
 
// 그래프 선 정의
var valueline = d3.svg.line()
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.close); });
    
// 캔버스 객체 생성
var svg = d3.select("body")
    .append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
    .append("g")
        .attr("transform"
              "translate(" + margin.left + "," + margin.top + ")");
 
// 2017-07-01 식으로 데이터를 해석하게 지정함
var parseDate = d3.time.format("%Y-%m-%d").parse;
 
// 전달 받은 데이터를 이용해서 그래프를 그린다.
var callback = function (data) {
    data.forEach(function(d) {
        d.date = parseDate(d.date);
        d.close = +d.close;
    });
 
    // 실데이터에 맞춰 그래프 범위 지정
    x.domain(d3.extent(data, function(d) { return d.date; }));
    y.domain([0, d3.max(data, function(d) { return d.close; })]);
 
    // 선 그리기.
    svg.append("path")
        .attr("class""line")
        .attr("d", valueline(data));
 
    // x축 그리기?
    svg.append("g")
        .attr("class""x axis")
        .attr("transform""translate(0," + height + ")")
        .call(xAxis);
 
    // y 축 그리기?
    svg.append("g")
        .attr("class""y axis")
        .call(yAxis);
 
};
 
// flask 에서 만든 http://127.0.0.1/data 를 호출하여 json 데이터를 가져와 callback 함수 호출
d3.json("/data", callback);
 
</script>
</body>
cs

 

  해당 코드를 역시 메모장에 복사하여, c:\python\flaskweb\templates 폴더에 utf-8 인코딩으로 d3sample.html 로 저장한다(한글 주석이 있어 utf-8 인코딩이 아니면 rendering 과정에서 에러가 난다).

 

 

  그럼 모든 코드가 구현되었고 c:\python\flaskweb\ 으로 이동하여, 아래와 같이 myweb_d3.py 파일을 실행 한다.

c:\Python\flaskweb>python myweb_d3.py
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 288-594-455
 * Running on
http://127.0.0.1:5000/ (Press CTRL+C to quit)

 

  이후 브라우저를 열어 http://127.0.0.1:5000/d3sample 이라고 치면, myweb_d3.py 상에서 d3sample 에 해당하는 showsample() 함수를 실행하여, d3sample.html 템플릿을 호출하면, 해당 템플릿 내에서 http://127.0.0.1:5000/data URL 을 호출하여, json 데이터를 받아서 D3.js 라이브러리를 이용해서 브라우저 화면에 아래와 같이 그래프를 출력하게 해준다(뭐 HTML 페이지에서 자바스크립트 d3.js 라이브러리를 이용해서 그래프를 그리는 것도 rendering 이라고 표현해도 된다).

 

 

  앞에서 설명한 방식으로 실제 동작하는지 확인하기 위해 이전 시간에 배운 피들러를 띄워 페이지 호출을 관찰해 보면, 아래와 같이 /d3sample 과 /data 가 차례로 호출되는 것을 볼 수 있다. 앞의 /d3sample 은 브라우저 주소창에서, 뒤의 /data 는 템플릿 랜더링 과정에서 호출한 것이다.

 

 

 

 

[matplotlib 그래프를 웹 페이지에 보여주기]

  마지막 예제는 위와 비슷한 데이터를 matplotlib 으로 그리고, HTML 페이지안에 해당 그림을 <img> 태그 형태로 삽입하는 예제이다.

 

 

[App Code 구현]

  예제를 구현하기 위해 구글에서 몇가지 샘플을 실행해 봤는데, python 2.x 대의 예제라서 라이브러리가 안 맞아 안 돌아 가거나, 3.x 대 예제인데, 실제로 에러는 안 나는데 이미지는 안 나오는(엑박표시) 경우가 많았었다. 몇 개의 예제를 검토해 본 바로는, 보통 두 가지 방식으로 구현 되는데, 첫째는 그려진 이미지를 static 폴더에 img 파일로 실제 물리적으로 저장한 후, html 템플릿 페이지를 띄워, 해당 이미지를 html 템플릿 내에 static 형식으로 포함하는 방법이 있고, 둘째는 <img> 태그 경로에 이미지를 생성하는 flask url 을 지정하여, 이미지를 마임(MIME) 데이터로 전달받아 브라우저에서 표시해 주는 방식이 있다. 여튼 결과적으로 'python 3 flask matplotlib html' 라고 검색해서 아래 2번째 방식으로 구현하는 예제를 찾았다.

http://dataviztalk.blogspot.kr/2016/01/serving-matplotlib-plot-that-follows.html

 

 

  해당 코드를 기반으로 데이터 생성만 간략화한 버전이 아래와 같다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
from io import BytesIO
from flask import Flask, render_template, send_file, make_response
import flask
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
import numpy as np
import matplotlib.pyplot as plt
 
app = flask.Flask(__name__)
 
# mypic 을 호출하면 mypic.html 로 렌더링 한다.
@app.route('/mypic')
def mypic():
    return flask.render_template("mypic.html")
 
# matplotlib 그래프 파일을 생성하여 소켓 통신으로 보내준다. 
@app.route('/plot')
def plot():
 
   # 그림판 준비
    fig, axis = plt.subplots(1)
    
   # 데이터 준비
    y = [1,2,3,4,5]
    x = [0,2,1,3,4]
 
    # 그리기
    axis.plot(x,y)
    canvas = FigureCanvas(fig)
    
    # 그려진 img 파일 내용을 html 랜더링 쪽에 전송한다.
    img = BytesIO()
    fig.savefig(img)
    img.seek(0)
    return send_file(img, mimetype='image/png')
 
 
if __name__ == '__main__':
    port = 5000
    app.debug = True
    app.run(port=port)
cs

 

  위의 코드를 메모장에 넣고, 파일형식은 '모든파일', 인코딩은 'utf-8' 로 선택하여, c:\python\flaskweb 폴더에 myweb_mat.py 로 저장한다.

 

 

[Templates Code 구현]

  그 다음은 mypic.html 템플릿 파일이다. 내부 코드는 엄청 간단해서 이미지 태그를 만들면서, 이미지 소스(src) 위치를 '/plot' 으로 지정한다. 그럼 http://127.0.0.1:5000/plot 을 읽어오며 실행되게 되어 해당 데이터가 MIME 데이터로 html 쪽에 전달되어 결합된다(사실 이런 방식은 처음 보는 거라서 좀 신기하긴 하다).

1
2
3
4
5
6
7
8
9
10
<html>
  <head>
    <title>image</title>
  </head>
  <body>
    matplotlib 으로부터 만들어진 이미지
    <p>
    <img src="/plot" alt="Image Placeholder">
  </body>
</html>
cs

 

  해당 코드를 역시 메모장에 복사하여, c:\python\flaskweb\templates 폴더에 utf-8 인코딩으로 mypic.html 로 저장한다.

 

 

  그럼 모든 코드가 구현되었고 c:\python\flaskweb\ 으로 이동하여, 아래와 같이 myweb_mat.py 파일을 실행 한다.

c:\Python\flaskweb>python myweb_mat.py
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 288-594-455
 * Running on
http://127.0.0.1:5000/ (Press CTRL+C to quit)

 

 

  이후 브라우저를 열어 'http://127.0.0.1:5000/mypic' 이라고 치면, myweb_mat.py 상에서 'mypic' url 에 해당하는 'mypic()' 함수를 실행하여, 'mypic.html' 템플릿을 호출하면, 해당 템플릿 내에서 'http://127.0.0.1:5000/plot' 경로를 호출하여, MIME 데이터로 이미지 스트림을 받아서 그림을 표시해 준다. 

 

 

 

 

 

[마무리 하면서]

  이렇게 해서 flask 프레임워크에 대해 간단히 살펴보는 시간이 끝났다. 'routing', 'dyanamic vs static web(static files)', 'rendering templetes' 가 주요 키워드인것 같다. 이전 시간에 legacy web 을 길게 설명하고, 이 시간에 flask 와 legacy web 을 비교하면서 얘기했던 방식이, 쉬운 이해에 도움이 되면 좋겠다. 여러 파이썬 웹 프레임워크에 대한 비교 글들의 끝에서 언급되지만 프레임워크의 선택은 개인의 일하는 스타일의 취향과, 구현하려는 목표에 해당 프레임워크가 추구하는 방향이 얼마나 적합한가에 따라 달라질 것이다. 실제 구현하려다 보면, 전체 구현하려는 대상에 비교해 프레임워크가 지원해 주는 기능은 정말 꼭 필요한 최소한의 기능 밖에 없는 것도 같긴 하다(물론 essential 한 부분이긴 하지만...). 

 

   추가로 d3.js 를 연계한 예제에서 봤듯이 웹의 많은 기능이 자바스크립트 기반에서 움직이기 때문에, 파이썬 로직으로 모든걸 해결하려는 것보다는, 파이썬 웹 어플리케이션 모듈 쪽에서는 데이터를 가공하여 제공하고, 실제 웹 쪽 UI 구현은 자바스크립트 라이브러리를 이용해 구현하면 효율이 좋은 경우도 많을 것 같다(웹의 1/3 쯤은 자바스크립트의 세상이고, d3.js 를 이해하기 위해서는 javascript 와 그래픽 라이브러리에 익숙하면 유리하기 때문에, 웹 전체에 연관되는 분야를 공부해 균형을 맞춰 놓는 것도 중요한 듯 하다). 다행인 점 하나는 'd3.js' 예제에서 봤듯이, 파이썬에서 배웠던 'matplotlib' 같은 목적이 비슷한 라이브러리를 사용해본 경험이, 많은 부분에서 비슷하게 적용 된다. 지금 이 시간이 기반이 되어 다음 시간인 Django 프레임워크에 대해서도 적절하게 설명할 수 있게 되길 바라며, Flask 살펴보기 시간을 마친다.  

 

 

 

 

2017.7.21 by 자유로운설탕
cs

 

  

 

 

 

 

 

 

 

 

posted by 자유로운설탕