[프로그래머스인공지능스쿨]Flask

4 minute read

이번주의 목표 Flask를 활용해서 REST API 구축하기
Django를 활용해서 웹 사이트 구축하기

1. Flask 시작하기

Flask?

  • Python 기반 “마이크로” 웹 프레임워크
  • “마이크로”란, 작지만 있을 건 다 있는 essential하다는 의미!
    우리의 로컬 컴퓨터에는 numpy, Matplotlib, … 등 다양한 모듈이 깔려있다.
    목적에 따른 모듈만 있는 환경을 구축해서 관리하면 편하지 않을까?
    -> 가상환경의 시작!

가상환경 설치하기

# 파이썬 가상환경 모듈 설치
pip install virtualenv
# 현재 디렉토리에 새 virtualenv 가상환경 만들기
virtualenv <가상환경 이름>

가상환경 이름은 통상적으로는 venv를 많이 사용한다.
만약 이때!! 다음과 같은 에러가 발생한다면

zsh: command not found: virtualenv
  1. pip 설치 : https://kalten.tistory.com/259
  2. python의 모듈로서 pip 호출 : https://beomi.github.io/2016/12/28/HowToSetup-Virtualenv-VirtualenvWrapper/
    UnicodeDecodeError: 'ascii' codec can't decode byte 0xe1 in position 26: ordinal not in range(128)
    

    ㅎㅎ..처음에 이 오류때문에 한참을 헤맸는데 한글이 포함된 디렉토리 안에서 virtualenv venv를 실행해서 그런거였다…
    꼭 한글이 포함되지 않은 디렉토리 하위에서 실행할 것!!
    다음과 같이 가상환경에 진입할 수 있다.

    source venv/bin/activate
    
    (venv) yeochaelin@yeochaecBookPro Desktop % 
    

    이렇게 앞에 (venv)가 붙으면 성공적으로 진입한 것!
    가상환경 안에 들어왔다는 것은 이 바깥의 세계와 독립된 파이썬 환경을 조성했다는 것이다.

    pip freeze
    

    이는 설치된 모듈을 보여주는 명령어인데, 아무것도 설치가 안 되어 있음을 알 수 있다.

    pip install flask
    

    이후 pip freeze를 입력하면 다음과 같이 flask가 잘 설치된 것을 볼 수 있다.

    click==7.1.2
    Flask==1.1.2
    itsdangerous==1.1.0
    Jinja2==2.11.2
    MarkupSafe==1.1.1
    Werkzeug==1.0.1
    

    근데 이후에 flask run을 진행하다보니

    zsh: command not found: flask
    

    와 같은 문제가 발생했다.
    https://stackoverflow.com/questions/51188027/terminal-error-flask-run-zsh-command-not-found-flask/52653822

    python3 -m pip install flask
    

    로 설치해서 해결했다.


Flask를 설치했으니 간단한 앱을 만들어보자.

# in ./app.py

from flask import Flask
app = Flask(__name__)

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

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

flask run으로 실행하면 된다.

(venv) yeochaelin@yeochaecBookPro Flask_Proj % flask run
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

http://127.0.0.1:5000/에 들어가보면 hello world!가 쓰여있는 웹페이지를 띄울 수 있다.


2. 인터넷과 웹

인터넷(Internet)

전 세계 컴퓨터를 하나로 합치는 거대한 통신망

웹(Web)

인터넷에 연결된 사용자들이 정보를 공유할 수 있는 공간
naver.com, google.kr, …

인터넷 이라는 집합 안에 웹이라는 부분집합이 있는 형태이다.
인터넷 안에는 메일이라는 또 다른 부분집합이 있다.
웹페이지의 집합을 웹 사이트라고 한다.

관찰 : 신발 가게의 동작방식

  1. 손님이 원하는 색상과 사이즈를 담은 신발을 요청한다.
  2. 직원이 해당 요청을 처리한다.
  3. 손님의 요청에 응답한다.

우리가 Web을 사용하는 방법

  1. 웹 브라우저(Chrome, Safari, …)를 켠다
  2. 주소창에 주소를 입력한다.
  3. Enter를 누르면 원하는 정보가 나온다!

Web의 동작방식

웹은 클라이언트와 서버 사이의 소통이다!
Client : 정보를 요청하는 입장
Server : 정보를 제공하는 입장

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


3. Flask with REST API

API?

  • 프로그램들이 서로 상호작용하는 것을 도와주는 매개체

Think RESTful!

Representational State Transfer
웹 서버가 요청을 응답하는 방법론 중 하나
데이터가 아닌, 자원(Resource)의 관점으로 접근
개발자 사이의 암묵적인 약속이다!

REST API?

HTTP URI를 통해 자원을 명시하고

  • HTTP URI : 웹 상에서 정보를 요청할 때 대상의 위치에 대한 식별자가 URI이다. URI에 속하는 것이 URL이다.
    HTTP Method를 통해 해당 자원에 대한 CRUD를 진행
  • HTTP Method : 정보를 요청하는 과정에서 쓰일 수 있는 여러가지 프로토콜(HTTP)약속으로 정해진 요청 방법들. GET, POST, PUT, …

REST API Example

HTTP Method Resource
GET /order
POST /order
PUT /order
DELETE /order

이 두가지의 조합을 통해서 같은 resource라도 다른 액션을 취할 수 있다.
REST API에서는 method와 resource를 사용한다.

REST API의 Stateless(무상태성)

Client의 Context를 서버에서 유지하지 않는다. 신경쓰지 않는다.
어떤 여러 클라이언트가 있을때, A가 서버에 보낸 요청, B가 서버에 보낸 요청, C가 서버에 보낸 요청은 모두 동일한 결과를 보내줘야 한다.
각각의 request를 독립적으로 간주한다.

REST API의 Stateless Example

POST/shoes는 자원에 새로운 정보를 생성
GET/shoes는 DB에서 shoes가 있는지 확인 후 해당 자원 반환
(서버 입장에서) 아이템을 GET하기 위해서 POST를 진행할 필요가 없음. shoes에서 어떤 자원이 있는지 체크하기 전에 POST를 했었는지 서버가 기억할 필요가 없다. 단지 주어진 행위(DB에서 shoes가 있는지 없는지 체크해 있으면 반화, 없으면 에러)를 취할 뿐. POST와 GET사이의 종속관계가 없다.
REST API는 이 때문에 협업에 장점이 있다.

Coffee Shop Menu API 구축

from flask import Flask, jsonify, request
# jsonify : 파이썬의 딕셔너리 타입(menus)을 json이라는 데이터 저장 방식으로 바꿔줌
# request : HTTP request를 다룰 수 있는 모듈
app = Flask(__name__) # Flask를 바탕으로 한 객체 생성. 인자로 __name__전달. Flask에 이름을 앱으로 넣어준다는 의미

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


# 홈 디렉토리
@app.route('/') # 이 주소를 요청받았을때 밑에 있는 함수를 실행하라.
def hello_code():
    return 'Hello World!'


# GET /menus : 자료를 가지고 온다
@app.route('/menus')
def get_menus():
    return jsonify({"menus" : menus})
# menus는 리스트로 json으로 변환할 수 없다. 
#따라서 menus를 value로 하는 새로운 딕셔너리를 만들어준다.


# POST /menus : 자료를 자원에 추가한다.
# @app.route는 기본적으로 methods=['GET']라고 되어있다.
# 나머지 HTTP 동작들, POST 등은 직접 명시를 해야한다.
@app.route('/menus', methods=['POST'])
def create_menu(): # request가 JSON이라고 가정
    # 전달받은 자료를 menus 자원에 추가
    request_data = request.get_json() # {"name" : ..., "price": ...}
    # request는 자동적으로 클라이언트가 서버에 POST로 요청할때 담긴 자료가 있다.
    # 따라서 이를 get_json()으로 파싱해주면 딕셔너리 형태로 담기게 된다.
    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()

4. Validation with Postman

Postman?

Postman을 통해 API를 테스트할 수 있다.
https://www.postman.com/downloads/
여기서 다운로드!
Collections -> +New Collection -> Name에 REST API with Flask 입력하고 Create!
png
png
이 API 상황을 다음과 같이 저장할 수 있다.
png
그럼 다음과 같이 collection에 우리가 설계한 API가 저장이 된다. 나중에 편하게 테스팅이 가능하다!
png
POST로 Body에서 직접 내용을 작성하여 새로운 메뉴를 추가해줄 수 있다. raw, JSON 타입을 선택해야 한다.

{
    "name" : "Dolce Latte",
    "price" : 5100
}

png
다음과 같이 새로 생긴 데이터를 확인할 수 있다.
png
그런데, POST를 여러번 해주면 중복된 데이터가 여러개 생기는 것을 GET을 통해 확인할 수 있다.
id가 여러개 있을 때 어떻게 처리해줘야 할까?
또, 서버를 껏다 키면 데이터가 사라질 수도 있다. 따라서 데이터베이스를 연동해서 사용해야 한다.