미니터에서 가장 먼저 구현해볼 기능은 회원가입입니다.
당연히 로그인을 해야 글을 쓸 수 있기 때문입니다.
이전에 만든 app.py
에 작성했습니다.
'''
필요한 정보 import
request : 사용자가 HTTP 요청을 통해 전송한 JSON 데이터를 읽어들일 수 있다.
jsonify : 딕셔너리 객체를 json으로 변환하여 HTTP 응답을 보낼 수 있다.
'''
from flask import (
Flask,
jsonify,
request
)
app = Flask(__name__)
#이전에 만든 Ping EndPoint
def ping...
...
'''
1. app.users{}
새로 가입한 사용자를 저장할 딕셔너리를 users란 변수에 정의
KEY는 사용자의 아이다가 되며, VALUE는 딕셔너리에 저장되어 있는 사용자 정보
2. app.id_count
사용자의 고유 id값을 저장하는 변수로 1부터 시작하며, 회원가입이 될 때 마다 1씩 증가한다.
'''
app.users = {}
app.id_count = 1
#엔드포인트의 URL과 HTTP method 정의
@app.route("/sign-up", methods=['POST'])
def sign_up() :
'''
1.
회원가입 요청을 한 json정보를 딕셔너리 형태로 변환해서 읽어들임
2.
회원가입 요청 정보에 id라는 키 값을 추가해준다.
3.
회원가입하는 사용자의 정보를 저장
예를 들어, 맨처음 가입하는 경우에는 app.users[1] = new_user라는 뜻인데,
내가 입력받은 유저의 정보는 new_user에 담겨있으므로 key를 1로 설정하여 저장한다는 뜻
4.
마지막으로 id_count에 1을 더해줘서, 두 번째로 가입하는 사람의 id는 2가 될 수 있도록 설정
'''
new_user = request.json #1
new_user['id'] = app.id_count #2
app.users[app.id_count] = new_user #3
app.id_count = app.id_count + 1 #4
return jsonify(new_user)
책에서는 httpie
를 사용했지만, 저는 Postman
을 사용했습니다.
보면 요청을 보낼 때, id
라는 key
값은 설정안했음에도 제가 코드에서 설정해줘서
결과에는 추가된 걸 확인할 수 있습니다.
제가 알기론 트위터가 한 번 글 쓸 때 140자정도로 알고 있는데 300자로 구현해보겠습니다.
(사실 SNS를 안해서 140자인지 정확히는 모르고 그렇다더라.. 라고 들었습니다)
300자를 초과한다면 400 Bad Request
응답을 보내야 합니다
우선 요청 데이터(request.json
) 형태는 다음과 같습니다
{
"id" : 1,
"tweet" : "첫 번째 트윗입니다. 안녕하세요"
}
그리고 구현해보면..
@app.route("/tweet", methods=['POST'])
def tweet() :
payload = request.json
'''
id는 숫자인데 왜 int를 붙여줄까요?,
왜냐하면 json을 통해 데이터를 받을 때 1이라고 입력하는 경우도 있지만,
"1"로 입력하여 보내는 경우도 있기 떄문입니다.
user_id라는 변수에는 id를 받고, tweet이라는 변수에서는 tweet을 받습니다.
'''
user_id = int(payload['id'])
tweet = payload['tweet']
# 내가 받은 user_id가 존재하는지 체크, 존재하지 않는다면 400 에러 발생
if user_id not in app.users :
return '사용자가 존재하지 않습니다', 400
# 트윗 글자수가 300글자가 넘어가는지 체크, 넘어간다면 400 에러 발생
if len(tweet) > 300 :
return '300자를 초과했습니다', 400
# 문제가 없다면 저장, 단 여기서 리스트로 감싸서 저장했는데, 이는 곧 구현할 타임라인 엔드포인트와 관련있다고 합니다.
app.tweets = [{
'user_id' : user_id,
'tweet' : tweet
}]
'''
저장 후 성공되었다는 메시지와 함께, 응답코드는 201
책에서는 200인데, 저는 201이 더 정확하다고 판단하여 201로 설정했습니다.
200 : 서버가 요청을 제대로 처리했다는 뜻이다. 이는 주로 서버가 요청한 페이지를 제공했다는 의미로 쓰인다.
201 : 성공적으로 요청되었으며 서버가 새 리소스를 작성했다.
출처 : 위키피디아
'''
return 'create tweeet', 201
왜 사용자가 존재하지 않는다고 나올까요?
제가 회원가입 테스트 후 서버를 껐기 때문입니다.
별도로 저장을 해줄 DB가 아직 없다보니, 서버가 꺼지면 다 날라가게 됩니다.
번거롭지만.. 다시 회원가입과 트윗 등록을 해보겠습니다.
이렇게 서버를 안 껐더니 정상적으로 잘 되는 걸 확인할 수 있습니다.
이번엔 서버를 켠 상태에서 팔로우 기능을 해보겠습니다.
팔로우의 요청 BODY 형태는 아래와 같습니다
#id가 1번인 사람이, id가 2번인 사람의 계정을 follow 하겠다.
{
"id" : 1,
"follow" : 2
}
#id가 1번인 사람이, id가 2번인 사람의 계정을 un-follow 하겠다.
{
"id" : 1,
"unfollow" : 2
}
@app.route("/follow", methods=['POST'])
def follow() :
payload = request.json
print(app.users)
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']
'''
사용자가 다른 사용자를 팔로우한 적이 있다면, 사용자의 follow 키와 연결되어 있는 set에 팔로우하고자 하는 사용자 아이디를 추가한다.
키가 존재하지 않으면 디폴트 값을 저장하고, 키가 존재하면 해당 값을 읽어 들이는 기능을 setdefault를 이용하여 구현
'''
user.setdefault('follow', set()).add(user_id_to_follow)
return jsonify(user)
우선, 포스트맨은 서버를 켜놔도 정보를 제대로 못 불러와서 httpie
를 사용했습니다
아래 명령어를 입력해서 설치해주면 됩니다.
pip install httpie
그 다음 회원가입만 2계정을 새로 해줬습니다.
데이터 입력형식은 아래와 같습니다.
http -v localhost:5000/sign-up email="이메일" name="이름" password="비밀번호"
그러면 id가 증가된 상태로 임의의 계정 2개가 생성된 것이 확인됩니다.
httpie
를 이용해서 follow
테스트를 해보겠습니다.
http -v POST localhost:5000/follow id=1 follow=2
테스트를 하면 아래와 같은 에러가 발생합니다
"POST /follow HTTP/1.1"
...
File "/Users/kylee/miniconda3/envs/wanted/lib/python3.8/json/encoder.py", line 179, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type set is not JSON serializable
이 에러는 set
을 파이썬 json
모듈이 JSON
으로 변환하지 못 했기 때문입니다.
그래서 JSON encoder
를 구현해서, set
을 list
로 변경해줌으로써
JSON
으로 문제없이 변경될 수 있도록 해야 합니다.
list
로 변경하는 이유는, list
는 JSON
으로 변경될 수 있기 때문입니다.
#flask.json에서 JSONEncode 클래스를 import
from flask.json import JSONEncoder
# import한 클래스를 상속받는 CustomJSONEncoder를 생성
# 기존의 JSONEncoder 클래스에 있는 default 메소드를 오버라이딩
class CustomJSONEncoder(JSONEncoder) :
# JSON으로 변경하고자 하는 객체가 set인 경우, list로 변경해서 리턴
def default(self, obj) :
if isinstance(obj, set) :
return list(obj)
# 객체가 set이 아닌 경우, 본래의 JSONEncoder 클래스의 default 메소드를 호출해서 리턴
return JSONEncoder.default(self, obj)
# 플라스크 디폴트 app encoder로 내가 만든 클래스를 지정
app.json_encoder = CustomJSONEncoder
이렇게 설정 후, 다시 실행해주면 팔로우가 무사히 된 걸 확인할 수 있습니다.
원래는 같은 계정에 대해 팔로우를 한 번 더 하면 삭제가 되어야 하는데,
아직 그 부분은 구현되지 않았고, 팔로우와 로직은 똑같으니 구현하지 않겠습니다.
또한 타임라인도 마찬가지로 넘어가고, 다음 챕터는 데이터베이스로 돌아오겠습니다.
출처 : 깔끔한 파이썬 탄탄한 백엔드(P103 - 124)