Flask는 파이썬으로 웹 애플리케이션을 구현할 때 사용되는 프레임워크다. 공식 홈페이지에 나와있는 설명으로는
micro web framwork 즉 군더더기가 없는 아주 가벼운 웹 프레임워크라는 이야기이다. 파이썬 웹 프레임워크에는 Django등 다른 여러 프레임워크가 있다. 하지만 플라스크(Flask)는 학습 곡선(learning curve)이 낮아 비교적 쉽게 배워서 사용할 수 있다.
플라스크는 파이참을 이용해 쉽게 실행할 수 있다.
단순 엔드포인트 구현
ㅇ]엔드포인트란 단순하게 설명하자면 API서버가 제공하는 통신 채널 혹은 접점이라고 할 수 있다.
프론트엔드 서버 등의 클라이언트가 백엔드 API서버와 통신할 때 엔드포인트에 접속하는 형태로 통신하게 된다.
각 엔드포인트는 고유의 URL 주소를 가지게 되며, 고유의 URL 주소를 통해 해당 엔드포인트에 접속할 수 있다.
일반적으로 각 엔드포인트는 고유의 기능을 담당하고 있다. 그리고 이러한 엔드포인트들이 모여 하나의 API를 구성하는 것이다.
from flask import Flask : Flask를 사용하기 위해 Flask를 임포트 해주는 부분
app = Flask(__name__) : 임포트한 Flask 클래스를 객체화(instantiate)시켜서 app이라는 변수에 저장 하였다.
이 app 변수가 바로 API 애플리케이션이다. app 변수에 API의 설정과 엔드포인트들을 추가하면 API가 완성되는 것이다.
이제 API통신을 어떻게 하는지 알아봤으니 기초적인 프로젝트를 구현하면서 학습을 해보자
기본 기능 정의
- 회원가입
- 로그인
- 게시글 작성
- 다른 회원 팔로우/언팔로우 하기
- 게시글 조회
회원가입
회원가입 절차는 사용자에게 이름, 이메일, 비밀번호 등의 기본적인 회원 정보를 받은 후 시스템상에 저장하면 된다.
from flask import Flask, jsonify, request
app = Flask(__name__)
app.users = {}
app.id_count = 1
@app.route("/sign-up", methods=['POST'])
def sign_up():
new_user = request.json
new_user["id"] = app.id_count
app.users[app.id_count] = new_user
app.id_count = app.id_count + 1
return jsonify(new_user)
딕셔너리를 users란 변수에 정의하고, 키는 사용자의 아이디, 밸류는 딕셔너리에 저장되어있는 사용자의 정보다
id_count는 회원가입 시 사용자의 id가 1씩 증가해야하한다.
다음과 같은 방법으로 회원가입을 만들면 HTTP요청들이 동시에 전송될 경우 id값이 잘못 지정될 가능성이 있다.
이러한 문제를 예방하기 위해서 atomic increment operation(여러 스레드가 동시에 증가시킬 수 없고, 한 번에 한 스레드만 값을 증가시키는 것)을 사용해야한다. 그러나 이번엔 간단한 연습을 위해 다음과 같이 구현하고 후에 데이터베이스를 연동하여 db에 데이터를 저장할 것이므로 db에서 id값을 자동 생성 해준다.
다음과 같이 JSON POST요청을 보내면 Response로 JSON형태를 받고 200OK를 확인할 수 있다.
게시글 작성
- 게시글에는 300자를 초과하지 않는 글을 올릴 수 있다
- 300자를 초과하면 400 Bad Request응답을 보내야 한다.
- 사용자가 300자 이내의 글을 작성하면 글을 저장하고 가지고 있다가 게시글 조회로 확인할 수 있다.
app.posts = []
@app.route("posts", methods=['POST'])
def posts():
payload = request.json
user_id = int(payload['id'])
content = payload['content']
if user_id not in app.users:
return '사용자가 존재하지 않습니다', 400
if len(content) > 300:
return '300자를 초과했습니다', 400
user_id = int(payload['id'])
app.posts.append({
'user_id' : user_id,
'content' : content
})
return '', 200
아직 만든 기능들의 데이터들이 db에 저장되는 것이 아닌 메모리상에만 저장되기때문에 서버를 껏다키면 모든 데이터가 초기화 된다.
팔로우 기능
@app.route('/follow', methods=['POST'])
def follow():
payload = request.json
user_id = int(payload['id'])
user_id_to_follow = int(payload['follow'])
if user_id not in app.users or user_id_to_follow not in app.users:
return '사용자가 존재하지 않습니다', 400
user = app.users[user_id]
user.setdefault('follow', set()).add(user_id_to_follow)
return jsonify(user)
만약 이상태로만 사용하면 jsonify가 set을 읽지 못하므로 set을 list로 변환해주는 인코더를 구현해서 디폴트 JSON인코더에 덮어 씌워야 한다.
from flask.json import JSONEncoder
class CustomJSONEncoder(JSONEncoder):
def default(self, obj):
if isinstance(obj, set):
return list(obj)
return JSONEncoder.default(self, obj)
app.json_encoder = CustomJSONEncoder
언팔로우는 add를 discard로 변경하면 끝
@app.route('/unfollow', methods=['POST'])
def unfollow():
payload = request.json
user_id = int(payload['id'])
user_id_to_follow = int(payload['unfollow'])
if user_id not in app.users or user_id_to_follow not in app.users:
return '사용자가 존재하지 않습니다', 400
user = app.users[user_id]
user.setdefault('follow', set()).discard(user_id_to_follow)
return jsonify(user)
remove 메소드가 아닌 discard메소드를 사용하는 이유 :
remove는 없는 값을 삭제하려면 오류가 발생하지만 discard는 값이 있으면 삭제 없으면 요청을 무시하는 메소드다
discard를 사용하면 언팔로우하려고자 하는 아이디가 실제로 set에 있는지 없는지를 검사하는 로직을 추가할 필요가 없다.
게시글 조회하기
게시글 조회의 경우 팔로우한 회원의 글만 가져오도록 구현 해보자
@app.route('/timeline/<int:user_id>', methods=['GET'])
def timeline(user_id):
if user_id not in app.users:
return '사용자가 존재하지 않습니다', 400
follow_list = app.users[user_id].get('follow', set())
follow_list.add(user_id)
timeline = [post for post in app.posts if post['user_id'] in follow_list]
return jsonify({
'user_id': user_id,
'timeline': timeline
})
api주소에서 <int:user_id>는 해당 위치에 적힌 값을 int로 변환해주고 user_id라는 데이터를 주소창에서 입력받아 사용하게 해준다. <Spring의 @Pathvariable과 비슷>
이런식으로 요청하면 1번의 타임라인을 보여주는 기능
이번 게시글에선 flask로 기초적인 api를 구현해보면서 가볍게 어떻게 사용하는지만 확인하였다.
전체 코드는 https://github.com/dss1222/flaskStudy_1/blob/master/app.py 이곳에서 확인 가능하다.