TIL DAY.40 [Flask] MiniTweet api 만들기

Dan·2020년 11월 4일
0

오늘은 Flask를 배우면서 처음으로 유명한 소셜사이트인 tweeter 기능 중 회원가입, follow, unfollow, tweet, 그리고 timeline 기능까지 api를 만들어 보았다.

송은우님의 깔끔한 파이썬 탄탄한 백엔드에 나오는 minitweet이라는 예제를 보고 따라했다. 따라하면서 이해했던 내용은 토대로 작성한 코드는 아래와 같다.

from flask import Flask, jsonify, request
from flask.json import JSONEncoder #flask.json 모듈에서 JSONEncoder 클래스를 임포트한다. JSONEncoder 클래스를 확장해서 커스텀 엔코더를 구현한다.

class CustomJSONEncoder(JSONEncoder): #JSONEncoder 클래스를 부모 클래스로 상속받는 CustomJsonEncoder 클래스를 정의한다.
    def default(self, obj):         #JSONEncoder 클래스의 default 메소드를 확장한다. Default 메소드에서 set인 경우 list로 변경해주어야 한다.
        if isinstance(obj,set):     
            return list(obj)        #JSON으로 변경하고자 하는 객체가 set인 경우 list로 변경해서 리턴한다.

        return JSONEncoder.default(self, obj)   #객체가 set이 아닌 경우는 본래 JSONEncoder 클래스의 default 메소드를 호출해서 리턴하면 된다.



app     = Flask(__name__)
app.users = {}
app.id_count = 1
app.tweets=[] #사용자들의 트웃들을 저장할 디렉터리다. key는 사용자 아이디이고, value는 사용자들이 트윗을 담고 있는 리스트 이다.
app.json_encoder = CustomJSONEncoder  #CustomJSONEncoder 클래스를 Flask의 디폴트 JSON 엔코더로 지정해준다. 그리하면 jsonify함수가 호출될떄마다 JSONEncoder가 아닌 CustomJSONEncoder 클래스가 사용된다.

@app.route('/timeline/<int:user_id>', methods = ['GET']) #엔드포인트 주소 <int:user_id>를 통해 사용자의 아이디를 지정할 수 있게 해준다.
def timeline(user_id):  #타임라인 엔드포인트를 구현하는 함수에 user_id를 인자로 받는데 이 값은 위에서 엔드포인트 주소 즉 사용자의 아이디이다.
    if user_id not in app.users:
        return '사용자가 존재하지 않습니다', 400
    
    follow_list = app.users[user_id].get('follow', set()) # 먼저 해당 사용자가 팔로우하는 사용자들 리스트를 읽어 들이고 만일 사용자가 다른 사용자를 팔로우 한적이 없는 경우 follow 필르를 empty set으로 리턴한다.
    follow_list.add(user_id) #팔로우하는 사용자 리스트에 해당 사용자의 아이디로 추가한다. 그러므로 팔로우하는 사용자들의 트윗뿐만 아니라 해당 사용자의 트윗도 볼 수 있다.
    timeline= [tweet for tweet in app.tweets if tweet['user_id'] in follow_list] #전체 트윗 중에 해당 사용자 그리고 해당 사용자가 팔로우 하는 사용자들의 트윗들만 읽어 들인다.

    return jsonify({    #사용자 아이디와 함께 타임라인을 JSON 형태로 리턴한다.
        'user_id' : user_id,
        'timeline': timeline
    })

@app.route('/tweet', methods=['POST']) #엔드포인트의 주소는 "/tweet" 이고 HTTP 메소드는 POST이다.
def tweet():
    payload = request.json 
    user_id = int(payload['id'])
    tweet   = payload['tweet']  #HTTP 요청으로 전송된 JSON 데이터에서 "tweet" 필드를 읽어 들여 사용자의 tweet내용이 300자를 넘었는지를 확인한다.

    if user_id not in app.users:  #만일 해당 사용자 아이디가 존재하지 않으면 400 Bad Request오류 메시지를 전송한다.
        return '사용자가 존재하지 않습니다', 400

    if len(tweet) > 300:  #만일 사용자의 트윗이 300자를 넘었으면 "300자를 초과했습니다" 라는 메시지와 함께 400 Bad  Request 응답을 보낸다.
        return '300자를 초과했습니다', 400

    user_id = int(payload['id']) #HTTP 요청으로 전송된 JSON 데이터에서 사용자 아이디를 읽어 들인다

    app.tweets.append({
        'user_id' : user_id, #해당 사용자 아이디와 트윗을 딕셔너리로 생성해서 app.tweet 리스트에 저장시킨다. 곧 구현 할 타임라인 엔드포인트에서 app.tweets 리스트를 읽어 들이게 된다.
        'tweet'   : tweet
    })

    return jsonify(app.tweets), 200

@app.route('/follow', methods = ['POST'])
def follow():
    payload         = request.json
    user_id         = int(payload['id']) #HTTP 요청으로 전송된 JSON 데이터에서 해당 사용자의 아이디를 읽어 들인다.
    user_id_to_follow = int(payload['follow']) #HTTP요청으로 전송된 JSON 데이터에서 해당 사용자가 팔로우할 사용자의 아이디를 읽어들인다
    print(user_id)
    print(user_id_to_follow)
    print(app.users)
    if user_id not in app.users or user_id_to_follow not in app.users: #만일 해당 사용자나 팔로우할 사용자가 존재하지 않는다면 400Bad Request 응답을 보낸다.
        return '사용자가 존재하지 않습니다', 400

    user = app.users[user_id]  
    user.setdefault('follow', set()).add(user_id_to_follow) #app.users 딕셔너리에서 해당 사용자 아이디를 사용해서 해당 사용자의 데이터를 읽어들인다.

    #setdefault는 키가 존재하지 않으면 디폴트값을 저장, 키가 존재하면 해당 값을 읽어 들이는 기능을 한다.
    #사용자를 저장하는 자료구조로 list 말고 set을 사용하는 이유는 이미 팔로우하고있는 사용자의 동일한 요청이 왔을 경우 한 사용자 아이디가 여러번 저장되지 않게 해주기 때문이다.

    return jsonify(user)

@app.route('/unfollow', methods=['POST'])
def unfollow():
    payload = request.json
    user_id = int(payload['id'])
    user_id_to_follow = int(payload['unfollow']) #언팔로우할 사용자 아이디를 HTTP 요청으로 전송된 데이터에서 읽어들인다.

    if user_id not in app.users or user_id_to_follow not in app.users: #팔로우 엔드포인트와 마찬가지로 해당 사용자 아이디 혹은 언팔로우할 사용자가 아이디가 존재하지 않으면 404응답을 보낸다.
        return '사용자가 존재하지 않습니다', 400

    user = app.users[user_id]
    user.setdefault('follow', set()).discard(user_id_to_follow) #언팔로우하고자 하는 사용자 아이디를 set에서 삭제한다. remove말고 discard 메소드를 사용하는 이유는 discard는 삭제하고자 하는 값이 있으면 삭제,없으면 무시를 하기 때문이다.

    return jsonify(user)

@app.route("/ping", methods=['GET'])
def ping():
    return "pong"

@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
    print(app.users)
    return jsonify(new_user)
    

이로써 처음으로 백엔드 api를 만들어보고 postman 과 터미널을 활용하여 결과를 확인하면서 백엔드가 하는 일에 대해서 조금은 이해 할 수 있는 시간이었다.

profile
만들고 싶은게 많은 개발자

0개의 댓글