<commit_before># Flask 기초

파이썬에서 백엔드를 담당하는 대표적인 microframework. [튜토리얼](http://flask.pocoo.org/docs/0.11/tutorial/introduction/)을 따라하면서 정리한다.

## 0. 웹 서버 개념

- 초기 웹서버는 단순히 정적인 파일을 클라이언트에게 전달하는 역할이었다.
- 동적으로 서버에서 어떤 연산을 해서 결과를 가공해서 주고싶었고, 그래서 나온게 **CGI**(Comman Gateway Interface)다.

![cgi](https://upload.wikimedia.org/wikipedia/commons/7/7c/CGI_common_gateway_interface.png)

- CGI는 단순히 클라이언트에서 어떤 입력(request)이 들어왔을 때 어떤 출력을 낼건지를 정의한 프로그램이다. 위 이미지에서처럼 예전엔 요청이 들어오면 CGI가 받아서, application server를 실행하는 형태로 이루어졌다.
- 이젠 서버 컴퓨터 성능이 좋아져서 web server가 CGI의 기능을 흡수했다. 그것이 **WAS**(Web Application Server)다. JSP나 ASP가 WAS의 예다.
- **WSGI**(Web Server Gateway Interface)는 파이썬에서만 쓰이는 WAS 통신규약이다. CGI의 superset으로 웹서버에서 받은 request에 대해 프로세스를 포크할건지 말건지를 정할 수 있다. 기존 CGI는 그냥 모든걸 포크한다.
- WSGI는 서버와 미들웨어 사이, 미들웨어와 앱 사이에 껴있다.
- 위 내용들을 알아서 처리해주는게 대표적으로 Flask와 Django다.

## 1. 기본

### 1.1 설치 및 띄워보기

- flask 설치: `pip install Flask`
- `hello.py` 파일을 만들고 `python hello.py` 실행

    ```py
    # hello.py
    from flask import Flask
    app = Flask(__name__)

    @app.route("/")
    def hello():
        return "Hello World!"

    if __name__ == '__main__':
        app.run('0.0.0.0', 5000, debug=True)
    ```

- `app`은 WSGI application을 의미
- `'0.0.0.0'` : 어떤 IP로 내 서버에 접속해도 오케이
- `debug=True` : 개발 모드로 서버를 실행하라는 의미로 파일이 변경되면 알아서 재시작해준다. 그리고 에러가 있을 때 디버깅이 쉽도록 브라우저에서 띄워준다. production에선 에러 정보가 잘못 사용될 여지가 있으므로 True 값을 주면 안됨

### 1.2 url로 변수 받기

```py
@app.route('/user/<username>')
def show_user_name(username):
    return f'User { user }'

@app.route('/post/<int:post_id>')
def show_post(post_id):
    return f'Post { post_id }'
```

- `<type:var_name>` : 이렇게 라우팅을 하면 URL의 부분을 변수로 받아서 사용할 수 있다.
- type : 자동으로 type casting해서 변수로 받아주고, 다른 타입이면 에러 일으킨다.
    + `string` : 기본값
    + `int` : 정수
    + `float` : 실수
    + `path` : 기본 string과 같지만 slash도 받는다
    + `any` : matches one of the items provided
    + `uuid` : accepts UUID strings

### 1.3 url_for

```py
from flask import Flask, url_for
app = Flask(__name__)

@app.route('/')
def index(): pass

@app.route('/login')
def login(): pass

@app.route('/user/<username>')
def profile(username): pass

with app.test_request_context():
    print(url_for('index')) # /
    print(url_for('login')) # /login
    print(url_for('login', non_exist_var='hihi')) # /login?non_exist_var=hihi
    print(url_for('profile', username="Gyubin Son")) # /user/Gyubin%20Son
```

- `url_for` : 지정한 매개변수들이 나타내는 URL을 문자열로 만들어서 리턴
- 첫 번째 매개변수인 문자열은 도메인 바로 다음에 오는, 파일에서는 함수명을 의미하고
- 두 번째로 오는 매개변수는 변수명과 값이 매칭되어서 URL이 된다
    + 변수가 함수 정의할 때 존재한다면 자연스럽게 URL이 만들어지고
    + 변수명이 존재하지 않는거라면 해당 pair가 query string으로 만들어진다.

## 2. 디렉토리 구조

- `/flaskr` : 
- `/flaskr/static` : CSS, JavaScript 파일이 위치
- `/flaskrtemplates` : Jinja2 템플릿이 위치.

## 3. Application module

`flaskr.py` 파일을 생성

### 3.1 config 파일 관리하기

```py
import os
import sqlite3
from flask import Flask, request, session, g, redirect,\
                  url_for, abort, render_template, flash

app = Flask(__name__)
app.config.from_object(__name__)

app.config.update(dict(
    DATABASE = os.path.join(app.root_path, 'flaskr.db'),
    SECRET_KEY='development key',
    USERNAME='admin',
    PASSWORD='<PASSWORD>'
))
app.config.from_envvar('FLASKR_SETTINGS', silent=True)
```

- 설정 파일을 관리한다. 순서대로 앱을 구해서, 기본 설정 파일을 가지고, 업데이트하고, 대체하는 코드다.
- 일반적으로 별도의 configuration file들(여러개 가능하다)을 가지고 있는 것이 좋은데 마지막 코드를 보자. `FLASKR_SETTINGS`는 설정 파일을 가리키고, silent 속성은 기존 config 객체에 매칭되는 키가 없더라도 오류메시지를 띄우지 않는다.
- 키는 항상 영문 대문자여야 한다.

### 3.2 DB connection

```py
def connect_db():
    rv = sqlite3.connect(app.config['DATABASE'])
    rv.row_factory = sqlite3.Row
    return rv
    
def get_db():
    if not hasattr(g, 'sqlite_db'):
        g.sqlite_db = connect_db()
    return g.sqlite_db

@app.teardown_appcontext
def close_db(error):
    if hasattr(g, 'sqlite_db'):
        g.sqlite_db.close()
```

- config 파일에서 설정된 DB와 연결한다.
- `rv.row_factory = sqlite3.Row` : row를 딕셔너리로 다룰 수 있게 한다.
- DB connection 싱글톤
    + DB connection은 긴 시간 지속되어야하고 오직 하나여야한다.
    + Flask는 application context, request context 두 가지를 제공한다. 전자가 `g` 모듈, 후자가 `request` 모듈과 관계있다.
    + 연결을 끊을 때는 `teardown_appcontext()` 데코레이터를 사용한다. 마킹된 함수는 application context가 tear down 될 때 항상 호출된다. 즉 app context는 request가 오기 전에 만들어지고, request가 끝날 때 사라진단 뜻이다.

## 4. Database Schema

### 4.1 Code

```py<commit_msg>global request<commit_after># Flask 기초

파이썬에서 백엔드를 담당하는 대표적인 microframework. [튜토리얼](http://flask.pocoo.org/docs/0.11/tutorial/introduction/)을 따라하면서 정리한다.

## 0. 웹 서버 개념

- 초기 웹서버는 단순히 정적인 파일을 클라이언트에게 전달하는 역할이었다.
- 동적으로 서버에서 어떤 연산을 해서 결과를 가공해서 주고싶었고, 그래서 나온게 **CGI**(Comman Gateway Interface)다.

![cgi](https://upload.wikimedia.org/wikipedia/commons/7/7c/CGI_common_gateway_interface.png)

- CGI는 단순히 클라이언트에서 어떤 입력(request)이 들어왔을 때 어떤 출력을 낼건지를 정의한 프로그램이다. 위 이미지에서처럼 예전엔 요청이 들어오면 CGI가 받아서, application server를 실행하는 형태로 이루어졌다.
- 이젠 서버 컴퓨터 성능이 좋아져서 web server가 CGI의 기능을 흡수했다. 그것이 **WAS**(Web Application Server)다. JSP나 ASP가 WAS의 예다.
- **WSGI**(Web Server Gateway Interface)는 파이썬에서만 쓰이는 WAS 통신규약이다. CGI의 superset으로 웹서버에서 받은 request에 대해 프로세스를 포크할건지 말건지를 정할 수 있다. 기존 CGI는 그냥 모든걸 포크한다.
- WSGI는 서버와 미들웨어 사이, 미들웨어와 앱 사이에 껴있다.
- 위 내용들을 알아서 처리해주는게 대표적으로 Flask와 Django다.

## 1. 기본

### 1.1 설치 및 띄워보기

- flask 설치: `pip install Flask`
- `hello.py` 파일을 만들고 `python hello.py` 실행

    ```py
    # hello.py
    from flask import Flask
    app = Flask(__name__)

    @app.route("/")
    def hello():
        return "Hello World!"

    if __name__ == '__main__':
        app.run('0.0.0.0', 5000, debug=True)
    ```

- `app`은 WSGI application을 의미
- `'0.0.0.0'` : 어떤 IP로 내 서버에 접속해도 오케이
- `debug=True` : 개발 모드로 서버를 실행하라는 의미로 파일이 변경되면 알아서 재시작해준다. 그리고 에러가 있을 때 디버깅이 쉽도록 브라우저에서 띄워준다. production에선 에러 정보가 잘못 사용될 여지가 있으므로 True 값을 주면 안됨

### 1.2 url로 변수 받기

```py
@app.route('/user/<username>')
def show_user_name(username):
    return f'User { user }'

@app.route('/post/<int:post_id>')
def show_post(post_id):
    return f'Post { post_id }'
```

- `<type:var_name>` : 이렇게 라우팅을 하면 URL의 부분을 변수로 받아서 사용할 수 있다.
- type : 자동으로 type casting해서 변수로 받아주고, 다른 타입이면 에러 일으킨다.
    + `string` : 기본값
    + `int` : 정수
    + `float` : 실수
    + `path` : 기본 string과 같지만 slash도 받는다
    + `any` : matches one of the items provided
    + `uuid` : accepts UUID strings

### 1.3 url_for

```py
from flask import Flask, url_for
app = Flask(__name__)

@app.route('/')
def index(): pass

@app.route('/login')
def login(): pass

@app.route('/user/<username>')
def profile(username): pass

with app.test_request_context():
    print(url_for('index')) # /
    print(url_for('login')) # /login
    print(url_for('login', non_exist_var='hihi')) # /login?non_exist_var=hihi
    print(url_for('profile', username="Gyubin Son")) # /user/Gyubin%20Son
```

- `url_for` : 지정한 매개변수들이 나타내는 URL을 문자열로 만들어서 리턴
- 첫 번째 매개변수인 문자열은 도메인 바로 다음에 오는, 파일에서는 함수명을 의미하고
- 두 번째로 오는 매개변수는 변수명과 값이 매칭되어서 URL이 된다
    + 변수가 함수 정의할 때 존재한다면 자연스럽게 URL이 만들어지고
    + 변수명이 존재하지 않는거라면 해당 pair가 query string으로 만들어진다.

## 2. 디렉토리 구조

- `/flaskr` : 
- `/flaskr/static` : CSS, JavaScript, 이미지, 음성 파일 등이 위치
    + `url_for('static', filename='style.css')` 형태로 불러쓸 수 있다.
- `/flaskrtemplates` : Jinja2 템플릿이 위치.

## 3. Application module

`flaskr.py` 파일을 생성

### 3.1 config 파일 관리하기

```py
import os
import sqlite3
from flask import Flask, request, session, g, redirect,\
                  url_for, abort, render_template, flash

app = Flask(__name__)
app.config.from_object(__name__)

app.config.update(dict(
    DATABASE = os.path.join(app.root_path, 'flaskr.db'),
    SECRET_KEY='development key',
    USERNAME='admin',
    PASSWORD='<PASSWORD>'
))
app.config.from_envvar('FLASKR_SETTINGS', silent=True)
```

- 설정 파일을 관리한다. 순서대로 앱을 구해서, 기본 설정 파일을 가지고, 업데이트하고, 대체하는 코드다.
- 일반적으로 별도의 configuration file들(여러개 가능하다)을 가지고 있는 것이 좋은데 마지막 코드를 보자. `FLASKR_SETTINGS`는 설정 파일을 가리키고, silent 속성은 기존 config 객체에 매칭되는 키가 없더라도 오류메시지를 띄우지 않는다.
- 키는 항상 영문 대문자여야 한다.

### 3.2 DB connection

```py
def connect_db():
    rv = sqlite3.connect(app.config['DATABASE'])
    rv.row_factory = sqlite3.Row
    return rv
    
def get_db():
    if not hasattr(g, 'sqlite_db'):
        g.sqlite_db = connect_db()
    return g.sqlite_db

@app.teardown_appcontext
def close_db(error):
    if hasattr(g, 'sqlite_db'):
        g.sqlite_db.close()
```

- config 파일에서 설정된 DB와 연결한다.
- `rv.row_factory = sqlite3.Row` : row를 딕셔너리로 다룰 수 있게 한다.
- DB connection 싱글톤
    + DB connection은 긴 시간 지속되어야하고 오직 하나여야한다.
    + Flask는 application context, request context 두 가지를 제공한다. 전자가 `g` 모듈, 후자가 `request` 모듈과 관계있다. request context는 `request`란 전역 객체로 언제 어디서나 접근할 수 있으며 항상 현재 수행되고 있는 스레드의 proxy로 동작. 즉 그 순간의 request로 매번 대입되기 때문에 편하게 사용하면 됨
    + 연결을 끊을 때는 `teardown_appcontext()` 데코레이터를 사용한다. 마킹된 함수는 application context가 tear down 될 때 항상 호출된다. 즉 app context는 request가 오기 전에 만들어지고, request가 끝날 때 사라진단 뜻이다.

## 4. Database Schema

### 4.1 Code

```py
{'fraction_non_alphanumeric': 0.12643547470153496, 'fraction_numerical': 0.009323479249573621, 'mean_word_length': 3.2823758519961053, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 4, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7536390', 'n_tokens_mistral': 5200, 'n_tokens_neox': 6217, 'n_words': 1104}