TIL - Flask

Qtory·2023년 9월 13일
0

AI

목록 보기
6/7
post-thumbnail

💽Flask

📚배운 강의 내용

  • Flask
  • flask를 사용해 기본적인 앱 작성하기
  • Example : Coffee Shop Menu API 구축
  • 과제 : CRUD(Create, Read, Update, Delete) 구현하기

📌Flask

Flask는 Python 기반 마이크로 웹 프레임워크이다. Django 같은 프레임워크라고 생각하면 좋다. 굉장히 가볍고 작은 프로젝트에 효율적이다.
flask를 설치하기 전에, 먼저 가상환경을 세팅하고자 한다. 다양한 모듈들을 한 환경에 설치해 관리하면 프로젝트끼리 상충되는 버전이 있을수도 있고 이래저래 귀찮은 일이 생길 수 있기 때문에 각 프로젝트마다 가상환경을 따로 관리하는 것이 편하다.

pip install virtualenv

cmd창을 열어 위 virtualenv를 다운로드 해주고 원하는 폴더에서 가상환경을 작동시킨다

virtualenv "가상환경이름"
source "가상환경이름"/bin/activate

위 명령어 역시 cmd창에서 작동시키면 이제 가상환경이 작동된 상태이다.

pip install flask

설치가 완료되었다. 이제 python파일을 통해 flask를 이용할 수 있다.


📌flask를 사용해 기본적인 앱 작성하기

from flask import Flask # flask 모듈로부터 Flask클래스를 불러온다.

app = Flask(__name__)

@app.route('/') # @ -> 데코레이터 #'/' 이 주소를 요청받으면 밑의 함수를 실행시킨다는 의미
def hello_flask():
	return "Hello World!"

if __name__ == '__main__':
	app.run()	# app이 module로서가 아니라 직접 실행될때 실행하라는 의미

위 코드까지 작성한 후 다시 cmd창에서

flaks run

이라고 실행하면

Running on  http://127.0.0.1:5000/

이라는 문구가 출력되는 것을 확인할 수 있다. 위 주소를 방문하게 되면 Hello World! 라고 적힌 페이지가 열리게 된다. 그렇다면 위 페이지는 어떤 방식으로 작동하게 된 것일까?

❗️Web의 동작방식

WebClientServer 사이의 소통이다. 웹의 동작방식은 다음과 같다.

1. Client가 Server에 정보를 요청한다. (HTTP Request)
2. Server는 요청받은 정보에 대한 처리를 진행한다.
3. Server가 Client에게 요청에 대해 응답한다. (HTTP Responce)

Client로부터 어떤 정보를 받았을 때 무슨 응답을 해줄지 결정해야 하는데 이를 돕는 것이 Flask와 Django이다.

❗️Flask with REST API

API? -> 프로그램들이 서로 상호작용하는 것을 도와주는 매개체
REST? -> Representational State Transfer 웹 서버가 요청을 응답하는 방법론 중 하나. 데이터가 아닌 자원(Resource)의 관점으로 접근
REST API? -> HTTP URI를 통해 자원을 명시하고 HTTP Method를 통해 해당 자원에 대한 CRUD를 진행

(URL \in URI)
(get, post, put 등등.. -> Method)

REST API의 Stateless: Client의 Context를 서버에서 유지하지 않는다.

Example. ->
POST/shoes는 자원에 새로운 정보를 생성한다.
GET/shoes 는 DB에서 shoes가 있는지 확인 후 해당 자원 반환
(서버 입장에서) 아이템을 GET하기 위해서 POST를 진행할 필요가 없다.


📌Example : Coffee Shop Menu API 구축

from flask import Flask, jsonify, request 
# jsonify: dict을 js에서 사용하는 저장방식으로 바꾸어줌.
# request: HTTP Request를 다루는 모듈

app = Flask(__name__)

menus = [
	{"id":1, "name":"Espresso", "price":3800},
    {"id":2, "name":"Americano", "price":4100},
    {"id":3, "name":"CafeLatte", "price":4600},
]

@app.route('/') # @ -> 데코레이터 #'/' 이 주소를 요청받으면 밑의 함수를 실행시킨다는 의미
def hello_flask():
	return "Hello World!"

# GET /menus # 자료를 가지고 올 때 사용
@app.route('/menus') # @app.route('주소', methods=['GET']) <- 기본형
def get_menus():
	return jsonify({"menus" : menus}) # jsonify를 사용해서 dict을 바꿔준다.

# POST /menus # 자료를 자원에 추가할 때 사용
@app.route('/menus', methods=['POST'])
def create_menu(): # request는 JSON이라고 가정
	# 전달받은 자료를 menus 자원에 추가
    request_data = request.get_json() # request_data -> dict형태 ({'name' : ..., 'price' : ...})
    new_menu = {
    	'id' : 4,
        'name' : request_data['name'],
        'price' : request_data['price'],
    }
	menus.append(new_menu)
    return jsonify(new_menu)
    

if __name__ == '__main__':
	app.run()	# app이 module로서가 아니라 직접 실행될때 실행하라는 의미

위 코드를 작성한 후, 위 주소값에 menus를 붙이면 GET 메소드가 실행된 것을 확인할 수 있다.

Running on  http://127.0.0.1:5000/menus

POST 메소드는 일반적인 웹 브라우저로 테스트하기에는 적절치 않다. 따라서 다른 프로그램의 도움을 받는데 보통 사용되는 툴은 POSTMAN이다.


❗️POSTMAN

https://www.postman.com/downloads/
위 주소를 통해 POSTMAN을 다운로드할 수 있다.


GET Method를 호출했을 때




POST Method를 호출하고 다시 GET Method를 호출했을 때



PUT Method를 호출했을 때(id=2)




DELETE Method를 호출했을 때(id=3)


📌과제 : CRUD(Create, Read, Update, Delete) 구현하기

메뉴 관리 CRUD 구현하기

  • HTTP 메서드 PUT 를 이용해 Update, DELETE 를 이용해 Delete 기능을 구현해주세요.
  • PUT /menu/<int:id> : 해당하는 id에 해당하는 데이터를 갱신합니다. (HTTPRequest의 Body에 갱신할 내용이 json으로 전달됩니다.)
  • DELETE /menu/<int:id> : 해당하는 id에 해당하는 데이터를 삭제합니다.
  • @app.route() 의 인자로 들어가는 경로에는 다음과 같이 사용해줄 수도 있습니다.
    @app.route('/<name>') # URL에 <>를 붙임으로서 이를 함수의 인자로 대입할 수 있습니다.
    def my_view_func(name):
       return name

Solution

from flask import Flask, jsonify, request

app = Flask(__name__)

menus = [
    {"id":1, "name":"Espresso", "price": 3800},
    {"id":2, "name":"Americano", "price": 4300},
    {"id":3, "name":"Caffe Latte", "price": 5000},
]
@app.route('/')
def hello_flask():
    return "Hello World!"

# GET /menus | 자료를 가지고 온다
@app.route('/menus')
def get_menus():
    return jsonify({"menus": menus})

# POST /menus | 자료를 자원에 추가한다
@app.route('/menus', methods = ['POST'])
def create_menu():
    # 전달받은 자료를 menus 자원에 추가
    request_data = request.get_json()
    new_menu = {
        "id": 4,
        "name": request_data['name'],
        "price": request_data['price'],
    }
    menus.append(new_menu)
    return jsonify(new_menu)

# UPDATE by PUT
@app.route('/menus/<int:id>', methods = ['PUT'])
def update_menu(id):
    request_data = request.get_json()
    for i in menus:
        if i['id'] == id:
            i["name"] = request_data['name']
            i["price"] = request_data['price']
    return jsonify(menus)

# DELETE by DELETE
@app.route('/menus/<int:id>', methods = ['DELETE'])
def delete_menu(id): 
    for i in menus:
        if i['id'] == id:
            menus.remove(i)
    return jsonify(menus)


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

❗️보너스 과제 I: ID야 움직여라 얍!

  • 새로운 menu를 추가하는 POST 영역에서 id가 4로 고정되어있는 문제가 발생합니다.
  • POST 요청이 들어올 때마다 id가 하나씩 증가하여 menu 리스트에 추가될 수 있도록 코드를 수정해주세요.
# POST /menus | 자료를 자원에 추가한다
@app.route('/menus', methods = ['POST'])
def create_menu():
    # 전달받은 자료를 menus 자원에 추가
    request_data = request.get_json()
    new_menu = {
        "id": len(menus)+1,	# post요청마다 길이보다 +1 높은 번호를 부여한다.
        "name": request_data['name'],
        "price": request_data['price'],
    }
    menus.append(new_menu)
    return jsonify(new_menu)

예외사항 (PUT, DELETE에서 없는 인자를 호출한 경우) 를 반영한 코드

from flask import Flask, jsonify, request

app = Flask(__name__)

menus = [
    {"id":1, "name":"Espresso", "price": 3800},
    {"id":2, "name":"Americano", "price": 4300},
    {"id":3, "name":"Caffe Latte", "price": 5000},
]


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


# GET /menus
@app.route('/menus')
def get_menus():
    return jsonify({"menus": menus})


# POST /menus
@app.route('/menus', methods = ['POST'])
def create_menu():
    request_data = request.get_json()
    new_menu = {
        "id": len(menus)+1,
        "name": request_data['name'],
        "price": request_data['price'],
    }
    menus.append(new_menu)
    return jsonify(new_menu)


# PUT
@app.route('/menus/<int:id>', methods = ['PUT'])
def update_menu(id):
    request_data = request.get_json()
    new_menu = {
        "id" : id,
        "name" : request_data['name'],
        "price" : request_data['price']
    }
    menus[find_index_by_id(menus, id)] = new_menu # # id가 존재한다면 업데이트한다.
    return jsonify(menus)


# DELETE
@app.route('/menus/<int:id>', methods = ['DELETE'])
def delete_menu(id): 
    del_index = find_index_by_id(menus, id) # id가 존재한다면 삭제한다.
    del_menu = menus[del_index]
    del(menus[del_index])
    return del_menu


def find_index_by_id(a, b):	# index가 존재할 때 반환하는 코드
    for i, item in enumerate(a):
        if item['id'] == b:
            return i
    raise ValueError(f"ID {b} not found in the list")


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

❗️보너스 과제 II : 데이터베이스 연동하기

  • 수업에서 다룬 API는 서버를 재시작하면 모든 정보가 리셋되는 치명적인 문제가 있었습니다. 이를 해결하기 위해 데이터만을 저장하는 데이터베이스를 도입하여 Flask과 연동할 필요가 생겼습니다.
  • SQL과 ORM 중 하나를 선택하여 데이터베이스와 Flask app을 연동해봅시다. (즉, 자원에 CRUD가 발생하면 이 정보가 데이터베이스에 저장되어야합니다.)

0개의 댓글