치악산 복숭아

[엘리스 AI트랙] 07-02-01 ~ 07-02-02 Flask 기초 1 본문

elice/토끼성장일지

[엘리스 AI트랙] 07-02-01 ~ 07-02-02 Flask 기초 1

Juliie 2021. 11. 9. 15:31

1. Flask Framework란?

  • 파이썬을 사용해서 웹 서버를 만들수 있게 도와주는 Web Framework
    • 파이썬의 패키지
  • micro framework
    • 기본적인 기능만 제공
  • Flask의 장점
    • 나만의 서버를 쉽게 작성 가능
    • 간단한 코드로 빠르게 실행 가능
    • 원하는 기능을 유연하게 확장하기 편리함

2. Flask로 Web server 만들기

from flask import Flask
app = Flask(__name__)

@app.route("/")
def home():
	return "hello world!"

if __name__ == "__main__":
	app.run()
  • __name__은 파일이 다른 파일에서 실행됐는지, 아니면 직접 실행했는지 알게 해줌 
  • app.route(url) 아래의 함수는 url에서 실행할 함수
  • if __name__ == "__main__"은 파일 이름이 main일 때만 app을 실행하도록 함

 


3. JSON 형식의 데이터 나타내기

from flask import Flask jsonify
app = Flask(__name__)

@app.route("/")
def home_json():
	blog_info = {"mountain":"chiak"}
    return jsonify(blog_info)

if __name__ == "__main__":
	app.run()

jsonify()

  • 딕셔너리 형태의 데이터를 전달함으로써 JSON 형태의 데이터를 화면에 전달

 


4. HTML 형식의 데이터 나타내기

  • 나타낼 html 파일을 templates 폴더에 넣어주어야 함
  • templates 폴더에 html 파일을 넣어놓으면 Flask가 자동으로 파일을 찾아서 연결해 줌

 

from flask import Flask, render_template
app = Flask(__name__)

@app.route("/")
def home_html():
	return render_template("index.html")
    
if __name__ == "__main__":
	app.run()
  • render_template templates 폴더 안의 html 파일을 불러와주는 역할을 함
  • trmeplates 폴더의 html 파일의 이름과 render_template() 안의 이름이 같아야 화면에 잘 전달할 수 있음

 


5. 여러가지 url 연결하기

@app.route("URL 주소")
  • 1개의 @app.route는 여러개의 함수를 가질 수 있지만 1개와 연결하는게 가장 깔끔
  • 함수의 이름은 중복될 수 없음
from flask import *
app = Flask(__name__)

@app.route("/")
def home():
	return jsonify("home")
    
@app.route("/admin")
def admin():
	return jsonify("admin page")

@app.route("/student")
def student():
	return jsonify("student Page")

@app.route("/student/<name>")
def user(name):
    user = {"name" : name}
    return jsonify(user)

if __name__ == "__main__":
	app.run()

 


6. REST API

  • HTTP URI을 통해서 데이터의 자원을 명시하고 HTTP Method(POST, GET, PUT, DELETE)를 통해 해당 자원에 대한 CRUD Operation을 적용하는 것
  • DB, 이미지, 텍스트 등의 다양한 데이터에 적용할 수 있음
  • 메시지가 의도하는 바를 URL에서 나타내므로, 쉽게 기능을 파악할 수 있음
  • HTTP 표준 프로토콜에 따르는 플랫폼에서 사용 가능
  • 서버와 클라이언트의 구분을 명확하게 할 수 있음
  • REST API의 표준이 존재하지 않음(➡️ 권고사항만 있음, 단점)

 

 


7. HTTP Method

  • GET과 POST 등이 있음
GET   POST
데이터를 URL 뒤에 ?와 함께 사용 사용방법 특정 양식(form)에 데이터를 넣어 전송하는 방법
  • 길이 제한 있음
  • 주소창에 정보가 노출되기 때문에 보안이 상대적으로 취약
단점  
http://사이트_주소?id=julie&pwd=1234 예시 http://사이트_주소

 

 

app.route()에 method 옵션 사용하기

# app.py

@app.route('url1', methods=["GET"])
@app.route('url2', methods=["POST"])
@app.route('url3', methods=["GET", "POST"])
  • app.route()에 methods라는 옵션을 추가해서 해당하는 HTTP Method만 사용할 수 있도록 적용할 수 있음
  • 이렇게 나누면 좋은 점: URL1과 URL2가 같아도 상관 X -> methods 옵션이 다르기 때문

 

GET 요청만 사용하기

from flask import *
app = Flask(__name__)

@app.route("/", methods=["GET"])
def home():
    # localhost:5000?name=elice
    name = request.args.get('name')
    # name == elice
    result = "hello " + name
    return result

if __name__ == "__main__":
	app.run()

 

POST 요청만 사용하기

<html>
<body>
	<form action='/login' method='post'>
    	<p>
        아이디: <input type='text' name='id'>
        </p>
        <p>
        비밀번호: <input type='password' name='pwd'>
        </p>
        <button type='submit'  />
    </form>
</body>
</html>
# app.py

from flask import *
app = Flask(__name__)

@app.route("/", methods=["GET"])
def home():
	return render_template("index.html")

@app.route("/login", methods=["POST"])
def post():
	id = request.form['id']
    pwd = request.form['pwd']
    if id == 'julie' and pwd = '1234':
    	return 'Hello Julie!'
    else:
    	return 'Go away'
        
if __name__ == "__main__":
	app.run()

 


8. Blueprint란?

  • API들을 분류 / 관리할 수 있게 해줌
  • Flask의 기능이 점점 늘어날수록, 자연스럽게 코드의 양 ⬆️
  • 이 때 Blueprint를 사용해서 길어진 코드를 모듈화
    • 수정 개발과 유지보수에 용이하게 코드 관리 가능

 

1) Blueprint를 사용하지 않았을 경우의 코드

  • app.route의 개수 == API의 개수
from flask import Flask, jsonify
app = Flask(__name__)

@app.route("/", methods=['GET'])
def home_route():
	return jsonify('home')
    
@app.route("/first", methods=['GET'])
def first_route():
	return jsonify('first page')
    
@app.route("/second", methods=['GET'])
def first_route():
	return jsonify('second page')
    
@app.route("/third", methods=['GET'])
def first_route():
	return jsonify('third page')

# ... 중략 ...

if __name__ == '__main__':
	app.run()

 

2) Blueprint를 사용했을 경우

# 1 app.py
# 오직 서버 실행에 집중

from flask import Flask
from first_api import bp

app = Flask(__name__)
app.register_blueprint(bp)

if __name__ == '__main__':
	app.run()
# 2 first_api.py
# API들을 따로 모아놓은 파일

from flask import Blueprint, jsonify
bp = Blueprint('bp', __name__)

@bp.route('/first', methods=['GET'])
def first_route():
	return jsonify('first page')

@bp.route('/second', methods=['GET'])
def second_route():
	return jsonify('second page')

 


9. Jinja2

  • 파이썬에서 가장 많이 사용되는 템플릿
  • 서버에서 받아온 데이터를 효과적으로 보여주고 비교적 간략한 표현으로 데이터를 가공 가능

1) Jinja2 Template에서 데이터 넘겨주기 - 단일변수

# app.py

@app.route("/")
def home():
	return render_template(
          'index.html',
          data = 'elice'
        )
<!-- index.html -->

<html>
    <head>
    	<title> jinja example </title>
    </head>
    <body>
    	{{ data }}
    </body>
</html>

 

2) Jinja2 Template에서 데이터 넘겨주기 - list

# app.py

@app.route("/")
def home():
	my_list = [1, 2, 3, 4, 5]
	return render_template(
          'index.html',
          data = my_list
        )
<!-- index.html -->

<html>
    <head>
    	<title> jinja example </title>
    </head>
    <body>
    	{{ data }}
        {% for d in data %}
        	{{ d }}
        {% endfor %} # if문이 끝날땐 endif
    </body>
</html>

 

3) Jinja2 Template에서 데이터 넘겨주기 - dictionary

# app.py

@app.route("/")
def home():
	my_data = {'name': 'elice'}
	return render_template(
          'index.html',
          data = my_data
        )
<!-- index.html -->

<html>
    <head>
    	<title> jinja example </title>
    </head>
    <body>
    	{{ data.get('name') }}
    </body>
</html>

 


8. 로그인 기능 구현

 

1) 쿠키

  • 클라이언트에 저장되는 키/값이 들어있는 데이터
    • 쿠키는 내 컴퓨터의 브라우저에 저장이 된다
  • 유효기간이 안에 저장되어 있으며 그 기간내에는 따로 삭제하지 않는한 계속 저장되어 있음
  • 사용자가 따로 요청하지 않아도, Request 시에 자동으로 서버에 전송

 

2) 세션

  • 쿠키를 기반으로 하지만 서버 측에서 관리하는 데이터
  • 클라이언트에 고유 ID를 부여하고 클라이언트에 알맞은 서비스 제공
  • 서버에서 관리하기 때문에 쿠키보다 보안이 우수함

 

  • http 통신의 한 사이클

  • http는 상태값을 저장하지 않음(stateless)
  • http의 이런 단점을 보완하기 위해 cookie를 담아서 전송하는 등의 방법 사용

 

3) 예제 코드

# 로그인

user_id = request.form['user_id']
user_pw = request.form['user_pw']
user = {'user_id' : 'julie', 'user_pw': '1234'}

if user is not None:
	if user_id == user['user_id'] and user_pw == user['user_pw']:
    	session['login'] = user.id
        return jsonify({"result":"success"})
    else:
    	return jsonify({"result":"fail"})
  • request로 받아온 로그인 정보(user_id, user_pw)를 변수에 저장
  • DB에 user_id와 가은 데이터가 있는지 찾아옴
  • 입력된 user_pw와 저장된 패스워드가 같은지 체크
# 로그아웃

session['login'] = None

 


9. 로깅

  • 프로그램이 작동할 때 발생하는 이벤트를 추적하는 행위
  • 프로그램의 문제들을 파악하고 유지보수 하는데 사용
  • 로깅을 통해 발생한 에러를 추적할 수 있음

1) 로거 레벨

  • DEBUG < INFO < WARNING < ERROR < CRITICAL
  • 기본 로거 레벨 세팅은 WARNING이기 때문에 설정 없이 INFO, DEBUG를 출력할 수 없음
  • 에러가 발생했을 때 Flask의 logger(기본 내장)를 사용하여 에러 확인 가능
DEBUG: 상세한 정보
INFO: 일반적인 정보
WARNING: 예상치 못하거나 가까운 미래에 발생할 문제
ERROR: 에러 로그, 심각한 문제
CRITICAL: 프로그램 자체가 실행되지 않을 수 있는 문제

 

2) 특정 에러 제어하기

@app.errorhandler(404)
def page_not_found(error):
    app.logger.error(error)
    return render_template("page_not_found.html")
  • app.route를 쓰는 것처럼 app.errorhandler를 사용하면 에러를 제어할 수 있다
Comments